{
    "openapi": "3.1.0",
    "info": {
        "title": "D-QR REST API",
        "version": "1.0.0",
        "description": "The REST API for D-QR.\n\n## Authentication\n\nMost endpoints take two headers: `X-Client-ID` with your client id, and `Authorization: Bearer <client_secret>`. Credentials are generated from the dashboard under Developer API and require a plan carrying `api_access`.\n\nThe mobile app authenticates differently: `POST /auth/login` returns an `mqt_` token which is sent as `Authorization: Bearer mqt_…` with no `X-Client-ID`. Both modes reach the same endpoints; which plans may use them differs.\n\n## Rate limiting\n\nAuthenticated endpoints allow 60 requests per minute keyed by client id, and carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. The `/auth/*` endpoints allow 10 per minute, because those are the ones a credential run hits. A 429 carries `Retry-After`.\n\n## Plan features\n\nSome endpoints additionally need a feature flag on the account — noted per endpoint as `x-plan-feature`. A token that authenticates fine still gets a 403 from those without it, so a 403 on a valid token is a plan question rather than a credential one.\n\n## Errors\n\nErrors are JSON with an `error` key. A 422 from a validated endpoint also carries `messages`, keyed by field — except in production, where the field names and rules are withheld because they are useful to whoever is probing the endpoint and to nobody else."
    },
    "servers": [
        {
            "url": "https://getdynamicqr.com/api/v1",
            "description": "This installation"
        }
    ],
    "tags": [
        {
            "name": "Mobile Authentication",
            "description": "Authenticate with the same email and password used on the website.\n\nNo API plan or special credentials required — any plan (including Free) can sign in.\nFeatures are gated by the user's current plan, identical to the web app.\n\nOn login/register, a mobile token (mqt_...) is returned.\nStore it securely and send it as: Authorization: Bearer <token>"
        },
        {
            "name": "QR Codes",
            "description": "Endpoints for managing QR codes programmatically."
        },
        {
            "name": "Bulk QR Codes",
            "description": "Create many QR codes in one request. The web app has had a CSV importer for\n\na while, but `bulk_qr` is a sold plan flag with no API behind it, so an\nintegration had to loop the single-create endpoint and wear the rate limit."
        },
        {
            "name": "Folders",
            "description": "Group QR codes into folders. Folders are first-class in the web app but were\n\nunreachable from the API, so an integration could not organise what it created."
        },
        {
            "name": "Dashboard & Analytics",
            "description": "Endpoints for dashboard overview stats and time-series scan analytics."
        },
        {
            "name": "Link Pages",
            "description": "Manage link-in-bio style landing pages.\n\nRequires the `link_pages` feature (Normal / Pro plan)."
        },
        {
            "name": "Business Cards",
            "description": "Manage digital business cards.\n\nRequires the `business_cards` feature (Normal / Pro plan)."
        },
        {
            "name": "Mobile Links",
            "description": "Manage short-URL mobile links (6-character codes).\n\nRequires the `mobile_links` feature (Normal / Pro plan)."
        },
        {
            "name": "Custom Domains",
            "description": "Serve short links from your own domain. Verification is a DNS TXT record,\n\nthe same flow the web app uses."
        },
        {
            "name": "Teams",
            "description": "Read access to the teams the caller owns or belongs to. Creating teams and\n\nmanaging invitations stays in the web app, where the seat-limit and billing\nflows live; this exists so an integration can see team context."
        },
        {
            "name": "Passes",
            "description": "Pass validation for the mobile app.\n\nThe same decision the web validator makes, so a venue can put half its staff\non phones and half on tablets without the two disagreeing about whether a\nticket has been used."
        },
        {
            "name": "Support",
            "description": "Manage support tickets / conversations.\n\nRequires the `support` feature (Pro plan or admin-enabled plans)."
        },
        {
            "name": "Billing & Subscriptions",
            "description": "Manage Paddle subscriptions from the mobile app.\n\nAll endpoints require a valid mobile token (Authorization: Bearer mqt_...)."
        },
        {
            "name": "User Profile",
            "description": "Endpoints for viewing and updating the authenticated user's profile."
        },
        {
            "name": "Public / Unauthenticated",
            "description": "Endpoints that require no authentication — used by the mobile app on startup\n\nand for anonymous QR creation before the user signs in."
        },
        {
            "name": "Admin",
            "description": "Admin-only endpoints. Require an admin mobile token."
        }
    ],
    "security": [
        {
            "clientId": [],
            "bearerAuth": []
        }
    ],
    "components": {
        "securitySchemes": {
            "clientId": {
                "type": "apiKey",
                "in": "header",
                "name": "X-Client-ID",
                "description": "Your client id. Not sent for mobile (`mqt_`) tokens."
            },
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Your client secret, or an `mqt_` mobile token."
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "string",
                        "description": "What went wrong, in one sentence."
                    },
                    "messages": {
                        "type": "object",
                        "description": "Per-field validation messages. Withheld in production.",
                        "additionalProperties": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        }
                    }
                },
                "required": [
                    "error"
                ]
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "No credentials, or credentials that are not valid.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "The account is inactive, or the plan does not carry this feature.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "NotFound": {
                "description": "No such record on this account. Another account's id reads as not found rather than forbidden.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "TooManyRequests": {
                "description": "Rate limit exceeded. See `Retry-After`.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        }
    },
    "paths": {
        "/admin/dashboard": {
            "get": {
                "operationId": "GetAdminDashboard",
                "summary": "Admin dashboard stats",
                "description": "Returns global platform statistics: user counts, scan trends, device\nbreakdown, top-performing QR codes, and recent registrations.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "range",
                        "in": "query",
                        "required": false,
                        "description": "Time window. One of: 24h, 7d (default), 30d, 3m, 1y.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "30d"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/qrcodes": {
            "get": {
                "operationId": "GetAdminQrcodes",
                "summary": "List / search all QR codes across the platform.",
                "description": "Every account's codes, for oversight. Ordinary customers reach only their own through\nGET /qrcodes.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "search",
                        "in": "query",
                        "required": false,
                        "description": "Name, UUID, or owner email.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "menu"
                    },
                    {
                        "name": "filter",
                        "in": "query",
                        "required": false,
                        "description": "all (default), active, inactive, anonymous.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "active"
                    },
                    {
                        "name": "type",
                        "in": "query",
                        "required": false,
                        "description": "QR type filter (url, whatsapp, wifi, etc.).",
                        "schema": {
                            "type": "string"
                        },
                        "example": "url"
                    },
                    {
                        "name": "sort",
                        "in": "query",
                        "required": false,
                        "description": "created_at (default), scans_count, name.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "scans_count"
                    },
                    {
                        "name": "dir",
                        "in": "query",
                        "required": false,
                        "description": "desc (default) or asc.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "desc"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number (default 1). 20 results per page.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "1"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/qrcodes/{id}": {
            "delete": {
                "operationId": "DeleteAdminQrcodesId",
                "summary": "Move a QR code to the trash. It stops resolving immediately and its owner",
                "description": "can restore it, with scan history intact, until the retention window ends.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetAdminQrcodesId",
                "summary": "Get a single QR code with full analytics.",
                "description": "Returns QR code details, scan stats, 30-day trend, device/OS/country\nbreakdowns, and the most recent 20 scans.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Not found"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/qrcodes/{id}/scans": {
            "get": {
                "operationId": "GetAdminQrcodesIdScans",
                "summary": "Paginated scan history for a single QR code.",
                "description": "Human scans only — bots are filtered out by the global scope on scans.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number (default 1). 30 results per page.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "1"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/qrcodes/{id}/toggle": {
            "patch": {
                "operationId": "PatchAdminQrcodesIdToggle",
                "summary": "Toggle a QR code's active status.",
                "description": "Switches the code between active and paused. A paused code redirects nobody and keeps\nits history and its link.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "QR code paused.",
                                    "data": {
                                        "is_active": false
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users": {
            "get": {
                "operationId": "GetAdminUsers",
                "summary": "List / search users",
                "description": "Every account, for oversight.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "search",
                        "in": "query",
                        "required": false,
                        "description": "Name or email search.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "ada"
                    },
                    {
                        "name": "filter",
                        "in": "query",
                        "required": false,
                        "description": "all (default), user, moderator, past_due, early_bird, coffee.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "moderator"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number (default 1). 20 results per page.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "1"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}": {
            "delete": {
                "operationId": "DeleteAdminUsersId",
                "summary": "Permanently delete a user and all their data.",
                "description": "**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Account deleted."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "You cannot delete your own account here."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/demote": {
            "patch": {
                "operationId": "PatchAdminUsersIdDemote",
                "summary": "Demote a moderator back to regular user.",
                "description": "Returns a moderator to an ordinary customer account.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Ada Lovelace has been changed to a regular user."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Only moderators can be demoted."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/plan": {
            "patch": {
                "operationId": "PatchAdminUsersIdPlan",
                "summary": "Change a user's plan.",
                "description": "Writes the stored `plan` column. It is not the whole story of what a customer has —\nlifetime access, a paid or granted trial and inherited team access all live outside it,\nwhich is why the account payload reports both `plan` (in effect) and `base_plan` (stored).\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "plan": {
                                        "type": "string",
                                        "description": "freemium, normal, or pro."
                                    }
                                },
                                "required": [
                                    "plan"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Ada Lovelace plan changed to pro."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Admin plan cannot be changed."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/promote": {
            "patch": {
                "operationId": "PatchAdminUsersIdPromote",
                "summary": "Promote a regular user to moderator.",
                "description": "**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Ada Lovelace has been promoted to Moderator."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Only regular users can be promoted to moderator."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/reset-password": {
            "post": {
                "operationId": "PostAdminUsersIdResetPassword",
                "summary": "Reset a user's password and email them the new one.",
                "description": "Generates a new password and emails it to the customer. It is never returned here.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "A new password has been emailed to Ada Lovelace."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Use the normal password-change flow for your own account."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/toggle-status": {
            "patch": {
                "operationId": "PatchAdminUsersIdToggleStatus",
                "summary": "Toggle a user's active status (activate / deactivate).",
                "description": "An inactive account is refused at every door, including this API.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "User has been deactivated."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "You cannot deactivate yourself."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/admin/users/{id}/trial": {
            "delete": {
                "operationId": "DeleteAdminUsersIdTrial",
                "summary": "Revoke an active admin trial.",
                "description": "Ends a granted trial now. It does not restore a paid trial's one-per-account allowance.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Admin trial revoked for Ada Lovelace."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Ada Lovelace has no active admin trial to revoke."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostAdminUsersIdTrial",
                "summary": "Assign an admin trial to a user.",
                "description": "Whether the customer is emailed about it follows the `trial_email_enabled` setting:\nthis door may be quieter than the admin panel, never louder. The trial is applied and\naudited either way.\n\n**Admin only.** The authenticated account must have the admin role.",
                "tags": [
                    "Admin"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The user ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "8"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "trial_plan": {
                                        "type": "string",
                                        "description": "normal or pro.",
                                        "example": "pro"
                                    },
                                    "trial_duration": {
                                        "type": "string",
                                        "description": "1d, 3d, 7d, 14d, 1m, 2m, 3m, 6m, 12m.",
                                        "example": "14d"
                                    }
                                },
                                "required": [
                                    "trial_plan",
                                    "trial_duration"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Ada Lovelace granted a Pro trial until Oct 4, 2026."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Cannot assign a trial to an admin."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/analytics": {
            "get": {
                "operationId": "GetAnalytics",
                "summary": "Scan analytics (all QR codes)",
                "description": "Returns detailed scan analytics across all QR codes, broken down by\ntime, device, OS, and country.\n\n**Requires the `analytics` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Dashboard & Analytics"
                ],
                "parameters": [
                    {
                        "name": "range",
                        "in": "query",
                        "required": false,
                        "description": "Time window. One of: 24h, 2d, 7d, 30d, 3m, 6m, 1y. Default: 7d.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "30d"
                    },
                    {
                        "name": "qr_id",
                        "in": "query",
                        "required": false,
                        "description": "Filter to a single QR code.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "analytics",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/auth/forgot-password": {
            "post": {
                "operationId": "PostAuthForgotPassword",
                "summary": "Request password reset",
                "description": "Sends a password reset link to the given email address (same as the web app).\nAlways returns 200 to avoid email enumeration.",
                "tags": [
                    "Mobile Authentication"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "email": {
                                        "type": "string",
                                        "description": "Account email address."
                                    }
                                },
                                "required": [
                                    "email"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "If that email exists, a reset link has been sent."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 10,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/auth/login": {
            "post": {
                "operationId": "PostAuthLogin",
                "summary": "Sign in",
                "description": "Authenticate with your website email and password.\nReturns a mobile token valid for 1 year.\n\nIf the account has 2FA enabled, first call this without `two_factor_code` —\nyou will receive HTTP 202 `{\"requires_two_factor\": true}`.\nThen call again with the 6-digit TOTP code included.",
                "tags": [
                    "Mobile Authentication"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "email": {
                                        "type": "string",
                                        "description": "Your account email.",
                                        "example": "user@example.com"
                                    },
                                    "password": {
                                        "type": "string",
                                        "description": "Your account password."
                                    },
                                    "two_factor_code": {
                                        "type": "string",
                                        "description": "6-digit TOTP code (only when prompted)."
                                    },
                                    "device_name": {
                                        "type": "string",
                                        "description": "Optional device label stored with the token."
                                    }
                                },
                                "required": [
                                    "email",
                                    "password"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "202": {
                        "description": "Response",
                        "content": {
                            "application/json": {
                                "example": {
                                    "requires_two_factor": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Invalid email or password"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 10,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/auth/logout": {
            "post": {
                "operationId": "PostAuthLogout",
                "summary": "Sign out",
                "description": "Revokes the mobile token used in this request.\nRequires Authorization: Bearer <token> header.",
                "tags": [
                    "Mobile Authentication"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Signed out successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/auth/register": {
            "post": {
                "operationId": "PostAuthRegister",
                "summary": "Create account",
                "description": "Register a new account using the same process as the website.\nReturns a mobile token immediately so you can start using the app.\nA verification email is sent in the background.",
                "tags": [
                    "Mobile Authentication"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "Your display name. Max 100 chars."
                                    },
                                    "email": {
                                        "type": "string",
                                        "description": "Email address. Must be unique."
                                    },
                                    "password": {
                                        "type": "string",
                                        "description": "Password, minimum 8 characters."
                                    },
                                    "password_confirmation": {
                                        "type": "string",
                                        "description": "Must match password."
                                    },
                                    "device_name": {
                                        "type": "string",
                                        "description": "Optional device label."
                                    }
                                },
                                "required": [
                                    "name",
                                    "email",
                                    "password",
                                    "password_confirmation"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "403": {
                        "description": "Forbidden",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Registration is currently closed"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 10,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/billing": {
            "get": {
                "operationId": "GetBilling",
                "summary": "Get billing status",
                "description": "Returns the authenticated user's current plan, subscription details,\nand available Paddle price IDs for in-app upgrade prompts.",
                "tags": [
                    "Billing & Subscriptions"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/billing/cancel": {
            "post": {
                "operationId": "PostBillingCancel",
                "summary": "Cancel subscription",
                "description": "Schedules cancellation at the end of the current billing period.\nAccess is retained until `ends_at`.",
                "tags": [
                    "Billing & Subscriptions"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "...",
                                    "ends_at": "..."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "No active subscription found."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/billing/checkout-url": {
            "post": {
                "operationId": "PostBillingCheckoutUrl",
                "summary": "Generate a checkout URL",
                "description": "Creates a Paddle transaction for the chosen price ID and returns a hosted\ncheckout URL. Open this URL in the system browser — Paddle handles payment.\nOn completion the Paddle webhook updates the user's plan automatically.",
                "tags": [
                    "Billing & Subscriptions"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "price_id": {
                                        "type": "string",
                                        "description": "Paddle price ID (e.g. pri_01abc...).",
                                        "example": "pri_01hq8k2n4v6x"
                                    }
                                },
                                "required": [
                                    "price_id"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "checkout_url": "https://checkout.paddle.com/..."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    },
                    "503": {
                        "description": "Unavailable",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Payments are not configured."
                                }
                            }
                        }
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/billing/portal-url": {
            "post": {
                "operationId": "PostBillingPortalUrl",
                "summary": "Generate customer portal URL",
                "description": "Returns a one-time Paddle Customer Portal URL for managing the subscription\n(update payment method, view invoices, cancel). Open in the system browser.",
                "tags": [
                    "Billing & Subscriptions"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "portal_url": "https://..."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "No active subscription found."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/billing/reactivate": {
            "post": {
                "operationId": "PostBillingReactivate",
                "summary": "Reactivate subscription",
                "description": "Removes a scheduled cancellation, keeping the subscription active.",
                "tags": [
                    "Billing & Subscriptions"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "..."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "No scheduled cancellation found."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/business-cards": {
            "get": {
                "operationId": "GetBusinessCards",
                "summary": "List business cards",
                "description": "Business cards are the public /bc/{slug} pages.\n\n**Requires the `business_cards` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Business Cards"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "business_cards",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostBusinessCards",
                "summary": "Create a business card",
                "description": "The slug is fixed at creation — it is what the public URL is built from.\n\n**Requires the `business_cards` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Business Cards"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "slug": {
                                        "type": "string",
                                        "description": "Unique slug for the public URL."
                                    },
                                    "name": {
                                        "type": "string",
                                        "description": "Full name. Max 100 chars."
                                    },
                                    "job_title": {
                                        "type": "string",
                                        "description": "Job title. Max 100 chars."
                                    },
                                    "company": {
                                        "type": "string",
                                        "description": "Company name. Max 100 chars."
                                    },
                                    "email": {
                                        "type": "string",
                                        "description": "Contact email."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "description": "Phone number. Max 30 chars."
                                    },
                                    "website": {
                                        "type": "string",
                                        "description": "Website URL."
                                    },
                                    "bio": {
                                        "type": "string",
                                        "description": "Short bio. Max 400 chars."
                                    },
                                    "theme": {
                                        "type": "string",
                                        "description": "One of: default, dark, minimal."
                                    },
                                    "social_links": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        },
                                        "description": "Array of {platform, url} objects (max 8)."
                                    }
                                },
                                "required": [
                                    "slug",
                                    "name"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "business_cards",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/business-cards/{id}": {
            "delete": {
                "operationId": "DeleteBusinessCardsId",
                "summary": "Delete a business card",
                "description": "**Requires the `business_cards` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Business Cards"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Business card ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "business_cards",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetBusinessCardsId",
                "summary": "Get a business card",
                "description": "**Requires the `business_cards` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Business Cards"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Business card ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "business_cards",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchBusinessCardsId",
                "summary": "Update a business card",
                "description": "**Requires the `business_cards` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Business Cards"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Business card ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "Full name. Max 100 chars."
                                    },
                                    "job_title": {
                                        "type": "string",
                                        "description": "Job title. Max 100 chars."
                                    },
                                    "company": {
                                        "type": "string",
                                        "description": "Company name. Max 100 chars."
                                    },
                                    "email": {
                                        "type": "string",
                                        "description": "Contact email."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "description": "Phone number. Max 30 chars."
                                    },
                                    "website": {
                                        "type": "string",
                                        "description": "Website URL."
                                    },
                                    "bio": {
                                        "type": "string",
                                        "description": "Short bio. Max 400 chars."
                                    },
                                    "theme": {
                                        "type": "string",
                                        "description": "One of: default, dark, minimal."
                                    },
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Publish or unpublish the card."
                                    },
                                    "social_links": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        },
                                        "description": "Replaces the whole list. Array of {platform, url} objects, max 8."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "business_cards",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/custom-domains": {
            "get": {
                "operationId": "GetCustomDomains",
                "summary": "List custom domains",
                "description": "Each carries two independent flags: `is_verified` (DNS proved you control\nthe host) and `is_active` (short links should actually use it).",
                "tags": [
                    "Custom Domains"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 1,
                                            "domain": "go.example.com",
                                            "is_verified": true
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostCustomDomains",
                "summary": "Add a custom domain",
                "description": "Returns the DNS TXT record to publish. Call the verify endpoint once it\nhas propagated.\n\n**Requires the `white_label` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Custom Domains"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "domain": {
                                        "type": "string",
                                        "description": "Hostname, e.g. go.example.com"
                                    }
                                },
                                "required": [
                                    "domain"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "white_label",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/custom-domains/{id}": {
            "delete": {
                "operationId": "DeleteCustomDomainsId",
                "summary": "Remove a custom domain",
                "description": "Codes that were issued on this domain keep working — they resolve on the default\nhost again.",
                "tags": [
                    "Custom Domains"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The custom domain ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "3"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Domain removed."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchCustomDomainsId",
                "summary": "Activate or deactivate a verified domain",
                "description": "`is_verified` and `is_active` are independent: DNS proving you control the host is\none question, and whether short links should use it is another. Activating a domain\nthat has not been verified is refused.",
                "tags": [
                    "Custom Domains"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The custom domain ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "3"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Whether short links should use it."
                                    }
                                },
                                "required": [
                                    "is_active"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 3,
                                        "domain": "go.example.com",
                                        "is_verified": true,
                                        "is_active": true
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Verify the domain before activating it."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/custom-domains/{id}/verify": {
            "post": {
                "operationId": "PostCustomDomainsIdVerify",
                "summary": "Verify a custom domain",
                "description": "Looks up the TXT record and marks the domain verified when it matches.",
                "tags": [
                    "Custom Domains"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The custom domain ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "3"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "is_verified": true
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "TXT record not found or does not match."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/dashboard": {
            "get": {
                "operationId": "GetDashboard",
                "summary": "Dashboard overview",
                "description": "Returns aggregate counts and top-performing QR codes for the authenticated user.",
                "tags": [
                    "Dashboard & Analytics"
                ],
                "parameters": [
                    {
                        "name": "range",
                        "in": "query",
                        "required": false,
                        "description": "Time window for scan trend: 24h, 2d, 7d (default), 30d, 3m, 6m, 1y.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "30d"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/folders": {
            "get": {
                "operationId": "GetFolders",
                "summary": "List folders",
                "description": "Folders are how one person tidies their own work. They are independent of clients,\nwhich are who the work is for.",
                "tags": [
                    "Folders"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 1,
                                            "name": "Restaurants",
                                            "color": "#4f46e5",
                                            "qr_codes_count": 3
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostFolders",
                "summary": "Create a folder",
                "description": "Names are not required to be unique — two folders may share one.",
                "tags": [
                    "Folders"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "Folder name (max 100 chars)."
                                    },
                                    "color": {
                                        "type": "string",
                                        "description": "Hex colour, e.g. #4f46e5."
                                    }
                                },
                                "required": [
                                    "name"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 1,
                                        "name": "Restaurants"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/folders/{id}": {
            "delete": {
                "operationId": "DeleteFoldersId",
                "summary": "Delete a folder",
                "description": "The QR codes inside are kept and simply become unfiled.",
                "tags": [
                    "Folders"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The folder ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "7"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Folder deleted. 3 QR code(s) are now unfiled."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetFoldersId",
                "summary": "Get a folder",
                "description": "A folder belonging to another account reads as not found rather than forbidden.",
                "tags": [
                    "Folders"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The folder ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "7"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 1,
                                        "name": "Restaurants"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchFoldersId",
                "summary": "Update a folder",
                "tags": [
                    "Folders"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The folder ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "7"
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "New name."
                                    },
                                    "color": {
                                        "type": "string",
                                        "description": "New hex colour."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "id": 7,
                                        "name": "Menus",
                                        "color": "#4f46e5",
                                        "qr_codes_count": 3
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/folders/{id}/qrcodes": {
            "post": {
                "operationId": "PostFoldersIdQrcodes",
                "summary": "Move QR codes into a folder",
                "description": "Ids belonging to another account are simply not found rather than refused, so a\nmixed list moves what it may and reports the count.",
                "tags": [
                    "Folders"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The destination folder ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "7"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "qr_code_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "integer"
                                        },
                                        "description": "IDs of QR codes to move.",
                                        "example": "[12, 13, 14]"
                                    }
                                },
                                "required": [
                                    "qr_code_ids"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Moved 2 QR code(s)."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/link-pages": {
            "get": {
                "operationId": "GetLinkPages",
                "summary": "List link pages",
                "description": "Link pages are the public /p/{slug} pages. The list does not include each page's items;\nfetch one page to get those.\n\n**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostLinkPages",
                "summary": "Create a link page",
                "description": "The slug is fixed at creation and is what the public URL is built from, so it cannot be\nchanged afterwards — the address is printed on things.\n\n**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "description": "Page title. Max 100 chars."
                                    },
                                    "slug": {
                                        "type": "string",
                                        "description": "URL slug (a-z, 0-9, - _). Must be unique."
                                    },
                                    "bio": {
                                        "type": "string",
                                        "description": "Short bio/description. Max 300 chars."
                                    },
                                    "theme": {
                                        "type": "string",
                                        "description": "One of: default, dark, gradient, minimal, neon."
                                    },
                                    "bg_color": {
                                        "type": "string",
                                        "description": "Background hex color."
                                    },
                                    "text_color": {
                                        "type": "string",
                                        "description": "Text hex color."
                                    },
                                    "button_color": {
                                        "type": "string",
                                        "description": "Button hex color."
                                    }
                                },
                                "required": [
                                    "title",
                                    "slug"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/link-pages/{id}": {
            "delete": {
                "operationId": "DeleteLinkPagesId",
                "summary": "Delete a link page",
                "description": "**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetLinkPagesId",
                "summary": "Get a link page (with items)",
                "description": "**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchLinkPagesId",
                "summary": "Update a link page",
                "description": "The slug is deliberately not updatable.\n\n**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "description": "A new page title. Max 100 chars."
                                    },
                                    "bio": {
                                        "type": "string",
                                        "description": "Short description under the title. Max 300 chars."
                                    },
                                    "theme": {
                                        "type": "string",
                                        "description": "One of: default, dark, gradient, minimal, neon."
                                    },
                                    "bg_color": {
                                        "type": "string",
                                        "description": "Background hex colour."
                                    },
                                    "text_color": {
                                        "type": "string",
                                        "description": "Text hex colour."
                                    },
                                    "button_color": {
                                        "type": "string",
                                        "description": "Button hex colour."
                                    },
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Publish or unpublish the page. An unpublished page is a 404, not an explanation."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/link-pages/{id}/items": {
            "post": {
                "operationId": "PostLinkPagesIdItems",
                "summary": "Add an item to a link page",
                "description": "**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "label": {
                                        "type": "string",
                                        "description": "Link label. Max 100 chars."
                                    },
                                    "url": {
                                        "type": "string",
                                        "description": "Destination URL."
                                    },
                                    "icon": {
                                        "type": "string",
                                        "description": "Bootstrap icon class. Default: bi-link-45deg."
                                    }
                                },
                                "required": [
                                    "label",
                                    "url"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/link-pages/{id}/items/{itemId}": {
            "delete": {
                "operationId": "DeleteLinkPagesIdItemsItemid",
                "summary": "Delete a link page item",
                "description": "**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "itemId",
                        "in": "path",
                        "required": true,
                        "description": "Item ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchLinkPagesIdItemsItemid",
                "summary": "Update a link page item",
                "description": "**Requires the `link_pages` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Link Pages"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Link page ID.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "itemId",
                        "in": "path",
                        "required": true,
                        "description": "Item ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "label": {
                                        "type": "string",
                                        "description": "A new label for the link. Max 100 chars."
                                    },
                                    "url": {
                                        "type": "string",
                                        "description": "A new destination URL."
                                    },
                                    "icon": {
                                        "type": "string",
                                        "description": "Bootstrap icon class, e.g. bi-instagram."
                                    },
                                    "sort_order": {
                                        "type": "integer",
                                        "description": "Position in the list, lowest first."
                                    },
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Show or hide this one link without deleting it."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "link_pages",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me": {
            "get": {
                "operationId": "GetMe",
                "summary": "Get profile",
                "description": "Returns the full profile of the currently authenticated user,\nincluding plan info and available feature flags.",
                "tags": [
                    "User Profile"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchMe",
                "summary": "Update profile",
                "description": "Updates the user's display name, timezone, or language preference.",
                "tags": [
                    "User Profile"
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "New display name (max 100 chars)."
                                    },
                                    "timezone": {
                                        "type": "string",
                                        "description": "Valid PHP timezone string.",
                                        "example": "America/New_York"
                                    },
                                    "language": {
                                        "type": "string",
                                        "description": "Two-letter locale code (e.g. en, de, fr, ja)."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me/api-credential": {
            "get": {
                "operationId": "GetMeApiCredential",
                "summary": "Get API credential info",
                "description": "Returns the Client ID and last-used timestamp for the current credential.\nThe secret is never returned.",
                "tags": [
                    "User Profile"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "client_id": "qrc_…",
                                    "last_used_at": "…"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me/avatar": {
            "post": {
                "operationId": "PostMeAvatar",
                "summary": "Upload avatar",
                "description": "Uploads a new avatar image. The previous avatar is deleted automatically.\nAccepted formats: JPEG, PNG, GIF, WebP. Max size: 2 MB.",
                "tags": [
                    "User Profile"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "avatar": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "Image file."
                                    }
                                },
                                "required": [
                                    "avatar"
                                ]
                            }
                        },
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "avatar": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "Image file."
                                    }
                                },
                                "required": [
                                    "avatar"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me/change-password": {
            "post": {
                "operationId": "PostMeChangePassword",
                "summary": "Change password",
                "description": "Updates the user's password. Requires the current password for verification.\nAfter a successful change, a new API credential pair is issued and the\nprevious one is revoked (forces re-login on other devices).",
                "tags": [
                    "User Profile"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "current_password": {
                                        "type": "string",
                                        "description": "The current password."
                                    },
                                    "password": {
                                        "type": "string",
                                        "description": "New password (min 8 chars)."
                                    },
                                    "password_confirmation": {
                                        "type": "string",
                                        "description": "Must match password."
                                    }
                                },
                                "required": [
                                    "current_password",
                                    "password",
                                    "password_confirmation"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Password changed. New API credentials issued.",
                                    "client_id": "qrc_…",
                                    "client_secret": "qrs_…"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me/deletion-request": {
            "delete": {
                "operationId": "DeleteMeDeletionRequest",
                "summary": "Withdraw account deletion request",
                "description": "Cancels the user's pending deletion request. The account is unaffected.",
                "tags": [
                    "User Profile"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deletion request withdrawn."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "You have no pending deletion request."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetMeDeletionRequest",
                "summary": "Get account deletion request",
                "description": "Returns the user's pending account deletion request, or null when there\nis none. Use this to decide whether to show the request form or the\nwithdraw action.",
                "tags": [
                    "User Profile"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": null
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostMeDeletionRequest",
                "summary": "Request account deletion",
                "description": "Submits a request to permanently delete the account. An administrator\nreviews it; nothing is deleted at this point and the account keeps\nworking until the request is approved.\n\nApproval permanently erases the account, every QR code and its scan\nhistory, link pages, business cards, short links, folders and all\nuploaded images. It cannot be undone.",
                "tags": [
                    "User Profile"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "reason": {
                                        "type": "string",
                                        "description": "Why the account should be deleted (10–1000 chars)."
                                    },
                                    "confirm": {
                                        "type": "boolean",
                                        "description": "Must be true to acknowledge the deletion is permanent."
                                    }
                                },
                                "required": [
                                    "reason",
                                    "confirm"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "description": "Response",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "You already have a deletion request awaiting review."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/me/qr-logo": {
            "post": {
                "operationId": "PostMeQrLogo",
                "summary": "Upload QR logo",
                "description": "Uploads an image file to use as a QR code logo. Returns the public URL\nto pass as `logo_url` when creating or updating a QR code.\nAccepted formats: JPEG, PNG, GIF, WebP. Max size: 2 MB.",
                "tags": [
                    "User Profile"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "logo": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "Image file."
                                    }
                                },
                                "required": [
                                    "logo"
                                ]
                            }
                        },
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "logo": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "Image file."
                                    }
                                },
                                "required": [
                                    "logo"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "logo_url": "https://..."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/mobile-links": {
            "get": {
                "operationId": "GetMobileLinks",
                "summary": "List mobile links",
                "description": "Mobile links are the short /ml/{code} links. The code is generated on creation.\n\n**Requires the `mobile_links` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Mobile Links"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "mobile_links",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostMobileLinks",
                "summary": "Create a mobile link",
                "description": "Creates a short 6-character redirect link. The code is auto-generated.\n\n**Requires the `mobile_links` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Mobile Links"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "description": "Display title. Max 100 chars."
                                    },
                                    "destination_url": {
                                        "type": "string",
                                        "description": "Destination URL."
                                    }
                                },
                                "required": [
                                    "title",
                                    "destination_url"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "mobile_links",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/mobile-links/{id}": {
            "delete": {
                "operationId": "DeleteMobileLinksId",
                "summary": "Delete a mobile link",
                "description": "**Requires the `mobile_links` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Mobile Links"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Mobile link ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "mobile_links",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetMobileLinksId",
                "summary": "Get a mobile link",
                "description": "**Requires the `mobile_links` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Mobile Links"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Mobile link ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "mobile_links",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchMobileLinksId",
                "summary": "Update a mobile link",
                "description": "**Requires the `mobile_links` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Mobile Links"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Mobile link ID.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "description": "New title."
                                    },
                                    "destination_url": {
                                        "type": "string",
                                        "description": "New destination URL."
                                    },
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Enable or disable the link."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "mobile_links",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/passes/validate": {
            "post": {
                "operationId": "PostPassesValidate",
                "summary": "Check a pass and, when it is good, spend one admission.",
                "description": "Spends one admission when the pass is good. The check and the increment happen inside\na transaction against a locked row, so two doors scanning the same ticket in the same\nsecond cannot both admit.\n\nEvery attempt is recorded, refusals included — \"the door said it was already used\" is\na dispute somebody has to settle later. Staff can only validate passes belonging to\ntheir own account, and another account's pass reads as not found.\n\n**Requires the `pass_validation` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Passes"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "token": {
                                        "type": "string",
                                        "description": "The scanned value — a token or the URL a camera read."
                                    },
                                    "gate": {
                                        "type": "string",
                                        "description": "Which door this was, for the audit trail."
                                    }
                                },
                                "required": [
                                    "token"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Forbidden",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Ticket validation is not available on your plan."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "pass_validation",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/passes/{token}": {
            "get": {
                "operationId": "GetPassesToken",
                "summary": "Look at a pass without spending it.",
                "description": "The read-only twin of the validate endpoint, for showing a holder the state of their\nown ticket. Looking at a pass must never spend it, so nothing is redeemed and nothing\nis recorded.\n\nA pass belonging to another account is reported as not found rather than forbidden:\nconfirming that somebody else's ticket exists is itself a leak.\n\n**Requires the `pass_validation` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Passes"
                ],
                "parameters": [
                    {
                        "name": "token",
                        "in": "path",
                        "required": true,
                        "description": "The pass token, or the full /pass/{token} URL a camera read.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "ok": true,
                                        "result": "valid",
                                        "pass": {
                                            "label": "Ticket 104",
                                            "status": "Unused",
                                            "remaining": 1,
                                            "max_uses": 1
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Not found"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "pass_validation",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/public/config": {
            "get": {
                "operationId": "GetPublicConfig",
                "summary": "Mobile app configuration",
                "description": "Returns runtime configuration for the mobile app. Called once on startup.\nNo authentication required.\n\nResponse includes:\n - Paddle checkout settings (client token, price IDs, environment)\n - Anonymous QR feature flags and limits\n - App Store / Play Store URLs\n - Minimum required app version (for force-update prompting)\n - Mobile maintenance mode flag and message\n - Branding (app name, primary colour)",
                "tags": [
                    "Public / Unauthenticated"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/public/openapi.json": {
            "get": {
                "operationId": "GetPublicOpenapiJson",
                "summary": "OpenAPI specification",
                "description": "The whole REST API as an OpenAPI 3.1 document, generated from the\nendpoints themselves. Import it into Postman or Insomnia, render it with\nSwagger UI or Redoc, generate a typed client from it, or hand the URL to\nan assistant — it describes every endpoint, its parameters, its plan\nrequirement and its responses.\n\nNo authentication, deliberately: a specification names the endpoints, not\nthe data behind them, and every one of them still refuses a request with\nno token. Requiring credentials to read it would mean a developer has to\nbe integrated before their tooling can tell them how to integrate.",
                "tags": [
                    "Public / Unauthenticated"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/public/qrcodes": {
            "get": {
                "operationId": "GetPublicQrcodes",
                "summary": "List anonymous QR codes for a session",
                "description": "Retrieves all QR codes belonging to an anonymous session token.",
                "tags": [
                    "Public / Unauthenticated"
                ],
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "description": "The anonymous_token returned on creation.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "anon_9f2c81aa"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                },
                "security": []
            },
            "post": {
                "operationId": "PostPublicQrcodes",
                "summary": "Create an anonymous QR code",
                "description": "Create a URL QR code without an account.\nThe returned `anonymous_token` must be stored on device to retrieve or\nclaim the QR later. Anonymous QRs have no analytics, no editing, and expire\nafter the configured number of hours (default 24 h).",
                "tags": [
                    "Public / Unauthenticated"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "destination_url": {
                                        "type": "string",
                                        "description": "The URL the QR code should point to."
                                    },
                                    "name": {
                                        "type": "string",
                                        "description": "Optional label. Defaults to the URL hostname."
                                    },
                                    "anonymous_token": {
                                        "type": "string",
                                        "description": "Existing session token (so the server can enforce the per-session limit)."
                                    }
                                },
                                "required": [
                                    "destination_url"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "403": {
                        "description": "Forbidden",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Anonymous QR creation is disabled."
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many requests",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Anonymous QR limit reached (3 per session)."
                                }
                            }
                        }
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                },
                "security": []
            }
        },
        "/qrcodes": {
            "get": {
                "operationId": "GetQrcodes",
                "summary": "List all QR codes",
                "description": "Returns a paginated list of all QR codes belonging to the authenticated user.",
                "tags": [
                    "QR Codes"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostQrcodes",
                "summary": "Create a QR code",
                "description": "Supports all 14 QR types. URL-based types (url, video, social, app, pdf, coupon, menu)\nuse `destination_url`. Content types (wifi, vcard, email, sms, phone, text, whatsapp)\npass their fields inside `qr_meta` as a key-value object.\n\nqr_meta fields per type:\n  whatsapp: { phone, message }\n  wifi:     { ssid, password, encryption (WPA|WEP|nopass) }\n  vcard:    { name, phone, email, organization, title, website, address }\n  email:    { to, subject, body }\n  sms:      { phone, message }\n  phone:    { phone }\n  text:     { content }",
                "tags": [
                    "QR Codes"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "Label for the QR code."
                                    },
                                    "qr_type": {
                                        "type": "string",
                                        "description": "One of: url, whatsapp, wifi, vcard, email, sms, phone, text, video, social, app, pdf, coupon, menu. Default: url."
                                    },
                                    "destination_url": {
                                        "type": "string",
                                        "description": "Required for url/video/social/app/pdf/coupon/menu types."
                                    },
                                    "qr_meta": {
                                        "type": "object",
                                        "description": "Type-specific fields (see below)."
                                    },
                                    "color_foreground": {
                                        "type": "string",
                                        "description": "Hex colour for QR dots.",
                                        "example": "#1d4ed8"
                                    },
                                    "color_background": {
                                        "type": "string",
                                        "description": "Hex background colour.",
                                        "example": "#ffffff"
                                    },
                                    "frame_text": {
                                        "type": "string",
                                        "description": "Label text below QR frame."
                                    },
                                    "expires_at": {
                                        "type": "string",
                                        "description": "ISO-8601 expiry datetime."
                                    },
                                    "expired_url": {
                                        "type": "string",
                                        "description": "Fallback URL after expiry."
                                    },
                                    "design_preset": {
                                        "type": "string",
                                        "description": "A QR Studio look to create the code already wearing. Requires the qr_studio plan flag."
                                    },
                                    "design": {
                                        "type": "object",
                                        "description": "QR Studio fields, applied over design_preset (or over the studio defaults on their own). Requires the qr_studio plan flag."
                                    },
                                    "slug": {
                                        "type": "string",
                                        "description": "A memorable short link, e.g. `summer-menu`, in place of the uuid. Needs the custom_slug plan feature; sent without it, it is ignored rather than refused."
                                    },
                                    "dot_style": {
                                        "type": "string",
                                        "description": "Module shape: square, rounded (default), dots, classy, classy_rounded, extra_rounded."
                                    },
                                    "frame_style": {
                                        "type": "string",
                                        "description": "Frame around the code: none, simple (default), rounded, bold, banner, label_top."
                                    },
                                    "show_type_icon": {
                                        "type": "boolean",
                                        "description": "Draw the type's icon in the middle. Default: true. Ignored when the code carries a logo."
                                    }
                                },
                                "required": [
                                    "name"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "description": "Validation failed"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/bulk": {
            "post": {
                "operationId": "PostQrcodesBulk",
                "summary": "Create QR codes in bulk",
                "description": "Every row is validated first; rows that fail are reported and skipped\nwhile the rest are created, so one bad URL does not lose the batch.\n\n**Requires the `bulk_qr` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Bulk QR Codes"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "items": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        },
                                        "description": "Up to 100 rows."
                                    },
                                    "items[].name": {
                                        "type": "string",
                                        "description": "Display name."
                                    },
                                    "items[].destination_url": {
                                        "type": "string",
                                        "description": "Target URL."
                                    },
                                    "items[].qr_type": {
                                        "type": "string",
                                        "description": "One of url, whatsapp, email, sms, phone, vcard, text, wifi. Defaults to url."
                                    },
                                    "items[].color_foreground": {
                                        "type": "string",
                                        "description": "Hex colour, e.g. #4f46e5."
                                    },
                                    "folder_id": {
                                        "type": "integer",
                                        "description": "Put every created code in this folder."
                                    }
                                },
                                "required": [
                                    "items",
                                    "items[].name",
                                    "items[].destination_url"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "bulk_qr",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/render": {
            "post": {
                "operationId": "PostQrcodesRender",
                "summary": "Render a QR code (stores nothing)",
                "description": "Encodes your content into a QR image and returns it in the response\nbody. **Nothing is stored**: no QR code record is created, no UUID is\nminted, no redirect through this domain is involved, no audit entry is\nwritten, and the encoded content is not logged.\n\nEvery other endpoint here creates a *dynamic* code, which encodes a\n`/q/{uuid}` address on this domain and resolves to your destination on\neach scan — that is what lets a printed code be re-pointed later. This\none encodes your address directly, so the scanner never touches us. The\ntrade is the obvious one: **a code rendered here can never be re-pointed,\nand it records no scans.** Print it only when that is what you want.\n\nSend either `data` (the literal payload) or `qr_type` plus `content`\n(a type described by its fields). The types that resolve to a page this\napplication serves — social, app, coupon, menu, barcode, gift_giveaway —\nare refused here, because there is no version of them that encodes\nnothing of ours.\n\nFour advisory headers ride on the response, because an image body has\nnowhere to put them: `X-QR-Scannable` (`yes` or `warnings`),\n`X-QR-Warnings`, `X-QR-Contrast-Ratio`, and `X-QR-Frame-Omitted` on a PNG\nwhose design carries a frame or caption.\n\n**Requires the `api_access` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "QR Codes"
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "data": {
                                        "type": "string",
                                        "description": "The literal string to encode, max 2048 characters. Mutually exclusive with qr_type/content.",
                                        "example": "https://your-own-domain.example/offer"
                                    },
                                    "qr_type": {
                                        "type": "string",
                                        "description": "One of: url, whatsapp, wifi, email, sms, phone, vcard, text, pdf, video. Default: url."
                                    },
                                    "content": {
                                        "type": "object",
                                        "description": "The fields belonging to qr_type, using the same names the dashboard uses, e.g. {\"wifi_ssid\":\"Guest\",\"wifi_security\":\"WPA\"}. Also accepted at the top level. An unrecognised key is a 422."
                                    },
                                    "format": {
                                        "type": "string",
                                        "description": "svg (default) or png.",
                                        "example": "svg"
                                    },
                                    "size": {
                                        "type": "integer",
                                        "description": "Edge length in pixels. SVG 96-4096 (default 512); PNG 96-512 (default 512). For print, ask for SVG and scale it losslessly.",
                                        "example": "1024"
                                    },
                                    "design_preset": {
                                        "type": "string",
                                        "description": "A QR Studio look to draw it in. With neither this nor design, the code is drawn as classic_ink.",
                                        "example": "midnight_indigo"
                                    },
                                    "design": {
                                        "type": "object",
                                        "description": "QR Studio fields applied over design_preset, or over the studio defaults on their own."
                                    },
                                    "logo": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "An image for the centre (jpg, jpeg, png, gif, webp; max 2MB), sent as multipart/form-data. Read from the request and never written to disk."
                                    },
                                    "logo_path": {
                                        "type": "string",
                                        "description": "A path returned earlier by POST /me/qr-logo, of the form qr-logos/... Any other path is a 422."
                                    }
                                }
                            }
                        },
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "data": {
                                        "type": "string",
                                        "description": "The literal string to encode, max 2048 characters. Mutually exclusive with qr_type/content.",
                                        "example": "https://your-own-domain.example/offer"
                                    },
                                    "qr_type": {
                                        "type": "string",
                                        "description": "One of: url, whatsapp, wifi, email, sms, phone, vcard, text, pdf, video. Default: url."
                                    },
                                    "content": {
                                        "type": "object",
                                        "description": "The fields belonging to qr_type, using the same names the dashboard uses, e.g. {\"wifi_ssid\":\"Guest\",\"wifi_security\":\"WPA\"}. Also accepted at the top level. An unrecognised key is a 422."
                                    },
                                    "format": {
                                        "type": "string",
                                        "description": "svg (default) or png.",
                                        "example": "svg"
                                    },
                                    "size": {
                                        "type": "integer",
                                        "description": "Edge length in pixels. SVG 96-4096 (default 512); PNG 96-512 (default 512). For print, ask for SVG and scale it losslessly.",
                                        "example": "1024"
                                    },
                                    "design_preset": {
                                        "type": "string",
                                        "description": "A QR Studio look to draw it in. With neither this nor design, the code is drawn as classic_ink.",
                                        "example": "midnight_indigo"
                                    },
                                    "design": {
                                        "type": "object",
                                        "description": "QR Studio fields applied over design_preset, or over the studio defaults on their own."
                                    },
                                    "logo": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "An image for the centre (jpg, jpeg, png, gif, webp; max 2MB), sent as multipart/form-data. Read from the request and never written to disk."
                                    },
                                    "logo_path": {
                                        "type": "string",
                                        "description": "A path returned earlier by POST /me/qr-logo, of the form qr-logos/... Any other path is a 422."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK — the image bytes, Content-Type image/svg+xml or image/png"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "description": "Forbidden",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "REST API access is not available on your current plan.",
                                    "upgrade": "..."
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "There is no QR type called 'hologram'. This endpoint renders: url, whatsapp, ..."
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    },
                    "503": {
                        "description": "Unavailable",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "This install cannot produce PNG (no GD extension). Request format=svg."
                                }
                            }
                        }
                    }
                },
                "x-plan-feature": "api_access",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/{id}": {
            "delete": {
                "operationId": "DeleteQrcodesId",
                "summary": "Delete a QR code",
                "description": "Permanently deletes a QR code and all its scan records.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "message": "Deleted successfully"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "get": {
                "operationId": "GetQrcodesId",
                "summary": "Get a QR code",
                "description": "Returns a single QR code with its scan statistics.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Not found"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "patch": {
                "operationId": "PatchQrcodesId",
                "summary": "Update a QR code",
                "description": "Updates the mutable fields of a QR code. Only supplied fields are changed.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "description": "A new label."
                                    },
                                    "destination_url": {
                                        "type": "string",
                                        "description": "A new destination URL."
                                    },
                                    "is_active": {
                                        "type": "boolean",
                                        "description": "Enable or disable the QR code."
                                    },
                                    "expires_at": {
                                        "type": "string",
                                        "description": "ISO-8601 datetime or null to clear."
                                    },
                                    "slug": {
                                        "type": "string",
                                        "description": "A new short link. Needs the custom_slug plan feature; without it the field is ignored and the existing slug is preserved."
                                    },
                                    "color_foreground": {
                                        "type": "string",
                                        "description": "Hex colour for the modules.",
                                        "example": "#1d4ed8"
                                    },
                                    "color_background": {
                                        "type": "string",
                                        "description": "Hex background colour.",
                                        "example": "#ffffff"
                                    },
                                    "dot_style": {
                                        "type": "string",
                                        "description": "square, rounded, dots, classy, classy_rounded, extra_rounded."
                                    },
                                    "frame_style": {
                                        "type": "string",
                                        "description": "none, simple, rounded, bold, banner, label_top."
                                    },
                                    "frame_text": {
                                        "type": "string",
                                        "description": "Label under the code. Max 50 chars."
                                    },
                                    "show_type_icon": {
                                        "type": "boolean",
                                        "description": "Draw the type's icon in the middle."
                                    },
                                    "expired_url": {
                                        "type": "string",
                                        "description": "Where to send scans once the code has expired."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/{id}/analytics": {
            "get": {
                "operationId": "GetQrcodesIdAnalytics",
                "summary": "Get QR analytics (time-series)",
                "description": "Returns scan trend, device breakdown, OS breakdown, and top countries\nfor a single QR code over the selected time range.\n\n**Requires the `analytics` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    },
                    {
                        "name": "range",
                        "in": "query",
                        "required": false,
                        "description": "Time window: 24h, 2d, 7d (default), 30d, 3m, 6m, 1y.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "30d"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "analytics",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/{id}/image": {
            "get": {
                "operationId": "GetQrcodesIdImage",
                "summary": "Get QR code image (SVG)",
                "description": "Returns the raw SVG markup for the QR code image.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK — Content-Type: image/svg+xml"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/{id}/scans": {
            "get": {
                "operationId": "GetQrcodesIdScans",
                "summary": "Get paginated scan history",
                "description": "Returns individual scan records for a QR code, newest first.\n\n**Requires the `analytics` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number. Default: 1.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "1"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "analytics",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/qrcodes/{id}/webhook-deliveries": {
            "get": {
                "operationId": "GetQrcodesIdWebhookDeliveries",
                "summary": "List webhook deliveries",
                "description": "Every attempt is recorded, failures included, so \"the webhook is not firing\" is a\nquestion this endpoint can answer. The log is capped at the last 50 attempts per\ncode.\n\nRecent delivery attempts for this QR code's scan webhook, newest first.\nUse it to check whether your endpoint is actually receiving scans.",
                "tags": [
                    "QR Codes"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The QR code ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "42"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "succeeded": true,
                                            "status_code": 200,
                                            "duration_ms": 142,
                                            "created_at": "..."
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/support": {
            "get": {
                "operationId": "GetSupport",
                "summary": "List support conversations",
                "description": "The customer's own conversations, newest activity first. This is the same thread the\ndashboard shows and staff answer from.\n\n**Requires the `support` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Support"
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "support",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            },
            "post": {
                "operationId": "PostSupport",
                "summary": "Open a support ticket",
                "description": "Opens a conversation and posts its first message.\n\n**Requires the `support` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Support"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "subject": {
                                        "type": "string",
                                        "description": "Ticket subject. Max 200 chars."
                                    },
                                    "body": {
                                        "type": "string",
                                        "description": "Initial message. Max 5000 chars."
                                    }
                                },
                                "required": [
                                    "subject",
                                    "body"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "support",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/support/{id}": {
            "get": {
                "operationId": "GetSupportId",
                "summary": "Get a conversation with all messages",
                "description": "Includes the full message history. Another account's conversation reads as not found.\n\n**Requires the `support` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Support"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The conversation ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "12"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "support",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/support/{id}/reply": {
            "post": {
                "operationId": "PostSupportIdReply",
                "summary": "Reply to a conversation",
                "description": "Posting to a closed conversation reopens it.\n\n**Requires the `support` plan feature.** Without it this endpoint answers 403 even with valid credentials.",
                "tags": [
                    "Support"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The conversation ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "12"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "body": {
                                        "type": "string",
                                        "description": "Message body. Max 5000 chars."
                                    }
                                },
                                "required": [
                                    "body"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-plan-feature": "support",
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/teams": {
            "get": {
                "operationId": "GetTeams",
                "summary": "List teams",
                "description": "Every team the caller owns or is a member of, with their role in each.",
                "tags": [
                    "Teams"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 1,
                                            "name": "Acme",
                                            "slug": "acme",
                                            "role": "owner",
                                            "members_count": 4
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/teams/{id}": {
            "get": {
                "operationId": "GetTeamsId",
                "summary": "Get a team",
                "description": "Includes the member list. Only reachable by the owner or a member.",
                "tags": [
                    "Teams"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The team ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "2"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        },
        "/teams/{id}/qrcodes": {
            "get": {
                "operationId": "GetTeamsIdQrcodes",
                "summary": "List a team's QR codes",
                "description": "The codes filed to this team, rather than the personal codes of its members.",
                "tags": [
                    "Teams"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The team ID.",
                        "schema": {
                            "type": "integer"
                        },
                        "example": "2"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 1,
                                            "name": "Shared menu",
                                            "uuid": "..."
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/TooManyRequests"
                    }
                },
                "x-rate-limit": {
                    "limit": 60,
                    "minutes": 1
                }
            }
        }
    }
}
