{
    "openapi": "3.1.0",
    "info": {
        "title": "spoo.me",
        "description": "REST API for spoo.me \u2014 free and open-source URL shortening service serving 400k+ redirects/day.\n\nAuthenticate using either:\n- **API Key**: `Authorization: Bearer spoo_<your_key>`\n- **JWT Token**: `Authorization: Bearer <jwt>` (obtained via /auth/login)\n- **Session Cookie**: `access_token` cookie (set automatically on login)",
        "contact": {
            "name": "spoo.me",
            "url": "https://spoo.me/contact",
            "email": "support@spoo.me"
        },
        "license": {
            "name": "AGPL-3.0",
            "url": "https://github.com/spoo-me/spoo/blob/main/LICENSE"
        },
        "version": "1.0.0"
    },
    "paths": {
        "/health": {
            "get": {
                "tags": [
                    "System"
                ],
                "summary": "Health Check",
                "description": "Check the health of the application and its dependencies.\n\nPings MongoDB and Redis to determine overall system status:\n\n- **healthy** (200): Both MongoDB and Redis are reachable.\n- **degraded** (200): MongoDB is reachable but Redis is down or not configured.\n- **unhealthy** (503): MongoDB is unreachable -- the app cannot function.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: None",
                "operationId": "healthCheck",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/auth/login": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Login",
                "description": "Authenticate with email and password.\n\nReturns JWT access token and sets secure HTTP-only cookies for both\naccess and refresh tokens. The refresh token can be used at\n``POST /auth/refresh`` to obtain new tokens without re-authenticating.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 5/min, 50/day\n\n**Security**: Returns identical error for wrong email and wrong password\nto prevent user enumeration.",
                "operationId": "loginUser",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/LoginRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/LoginResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/auth/register": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Register",
                "description": "Create a new user account with email and password.\n\nImmediately signs the user in by returning a JWT access token and setting\nsecure HTTP-only cookies. A verification email is sent best-effort; the\n``verification_sent`` field indicates whether it succeeded.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 5/min, 50/day\n\n**Notes**: The account is created even if the verification email fails.\nThe user must verify their email before accessing protected resources.",
                "operationId": "registerUser",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/RegisterRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "201": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/RegisterResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict \u2014 resource already exists",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/auth/refresh": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Refresh Tokens",
                "description": "Rotate the access and refresh token pair.\n\nReads the ``refresh_token`` cookie, validates it, and issues a new\naccess/refresh pair. Both cookies are replaced. If the refresh token\nis missing, expired, or invalid, all auth cookies are cleared and a\n401 response is returned.\n\n**Authentication**: Requires a valid ``refresh_token`` cookie\n\n**Rate Limits**: 20/min\n\n**Notes**: The old refresh token is invalidated after use (rotation).",
                "operationId": "refreshTokens",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/RefreshResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/auth/logout": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Logout",
                "description": "Log the current user out by clearing auth cookies.\n\nRemoves the ``access_token`` and ``refresh_token`` HTTP-only cookies.\nAlways succeeds regardless of whether the user was authenticated.\n\n**Authentication**: Not required\n\n**Rate Limits**: 60/hour",
                "operationId": "logout",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/LogoutResponse"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/me": {
            "get": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Get Current User",
                "description": "Return the authenticated user's full profile.\n\nIncludes email, verification status, linked OAuth providers, plan,\nand profile picture. Useful for populating the UI after login or\non page load.\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 60/min",
                "operationId": "getCurrentUser",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MeResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/set-password": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Set Password",
                "description": "Set a password for an OAuth-only account.\n\nAllows users who signed up via OAuth to add a password so they can\nalso log in with email + password. Fails if the user already has a\npassword set.\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 5/min",
                "operationId": "setPassword",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/SetPasswordRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MessageResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/send-verification": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Send Verification Email",
                "description": "Send a 6-digit OTP verification code to the user's email.\n\nThe code expires after the duration returned in ``expires_in`` (seconds).\nIf the user is already verified, the request still succeeds but no email\nis sent.\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 3/hour\n\n**Notes**: Previous unused OTPs are invalidated when a new one is sent.",
                "operationId": "sendVerification",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/SendVerificationResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/verify-email": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Verify Email",
                "description": "Verify the user's email address using a 6-digit OTP code.\n\nOn success, new JWT tokens are issued with ``email_verified=true`` in the\nclaims, and auth cookies are updated. A welcome email is sent best-effort.\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 10/hour\n\n**Notes**: The OTP must match the most recently sent code and must not\nbe expired. Expired or already-used codes are rejected.",
                "operationId": "verifyEmail",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/VerifyEmailRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/VerifyEmailResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/request-password-reset": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Request Password Reset",
                "description": "Request a password-reset OTP to be sent via email.\n\nAlways returns the same success response regardless of whether the\nemail is registered. This prevents user enumeration attacks.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 3/hour\n\n**Security**: Timing-safe -- response time is constant whether or not\nthe account exists.",
                "operationId": "requestPasswordReset",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/RequestPasswordResetRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MessageResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/auth/reset-password": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Reset Password",
                "description": "Reset the account password using a 6-digit OTP code.\n\nThe OTP must have been requested via ``POST /auth/request-password-reset``.\nOn success the password is updated immediately and the OTP is consumed.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 5/hour\n\n**Notes**: Expired or already-used OTPs are rejected with a 400 error.",
                "operationId": "resetPassword",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ResetPasswordRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MessageResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/oauth/providers": {
            "get": {
                "tags": [
                    "OAuth"
                ],
                "summary": "List OAuth Providers",
                "description": "List all OAuth providers linked to the authenticated user's account.\n\nReturns each linked provider's name, email, and link date, plus whether\nthe user has a password set (needed by the UI to decide if unlinking\nthe last provider is allowed).\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 60/min",
                "operationId": "listOAuthProviders",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/OAuthProvidersResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/oauth/providers/{provider_name}/unlink": {
            "delete": {
                "tags": [
                    "OAuth"
                ],
                "summary": "Unlink OAuth Provider",
                "description": "Remove an OAuth provider link from the authenticated user's account.\n\nFails if the provider is the user's only authentication method (i.e.,\nno password set and no other providers linked).\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 5/min",
                "operationId": "unlinkOAuthProvider",
                "parameters": [
                    {
                        "name": "provider_name",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Provider Name"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/MessageResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/oauth/{provider}": {
            "get": {
                "tags": [
                    "OAuth"
                ],
                "summary": "OAuth Login",
                "description": "Initiate the OAuth authorization flow for the given provider.\n\nRedirects the user to the provider's consent screen (e.g., Google,\nGitHub). After the user grants access, the provider redirects back\nto ``GET /oauth/{provider}/callback``.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 10/min\n\n**Supported providers**: google, github (configurable)",
                "operationId": "initiateOAuthLogin",
                "parameters": [
                    {
                        "name": "provider",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Provider"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/oauth/{provider}/callback": {
            "get": {
                "tags": [
                    "OAuth"
                ],
                "summary": "OAuth Callback",
                "description": "Handle the OAuth provider callback after user authorization.\n\nValidates the CSRF state parameter, exchanges the authorization code for\nan access token, fetches the user's profile from the provider, and then\neither logs in an existing user or creates a new account. On success,\nredirects to ``/dashboard`` with JWT cookies set.\n\n**Authentication**: Not required (public endpoint)\n\n**Rate Limits**: 20/min\n\n**Notes**: This endpoint is called by the OAuth provider, not directly\nby the client. The ``state`` query parameter is required for CSRF protection.",
                "operationId": "oauthCallback",
                "parameters": [
                    {
                        "name": "provider",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Provider"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/oauth/{provider}/link": {
            "get": {
                "tags": [
                    "OAuth"
                ],
                "summary": "Link OAuth Provider",
                "description": "Initiate an OAuth flow to link a provider to the authenticated account.\n\nSimilar to ``GET /oauth/{provider}`` but includes the user's ID in the\nstate token so the callback knows to link rather than log in. The user\nmust already be authenticated.\n\n**Authentication**: Required (JWT or API key)\n\n**Rate Limits**: 5/min",
                "operationId": "linkOAuthProvider",
                "parameters": [
                    {
                        "name": "provider",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Provider"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/shorten": {
            "post": {
                "tags": [
                    "URL Shortening"
                ],
                "summary": "Create Shortened URL",
                "description": "Create a new shortened URL.\n\nCreate a shortened URL with optional customization including password protection,\nexpiration, click limits, and bot blocking.\n\n**Authentication**: Optional \u2014 higher rate limits when authenticated.\n\n**API Key Scope**: `shorten:create` or `admin:all`\n\n**Rate Limits**:\n- Authenticated: 60/min, 5,000/day\n- Anonymous: 20/min, 1,000/day\n\n**Anonymous Usage Consequences**:\n- Lower rate limits\n- Cannot manage or view URLs later\n- Cannot use private stats\n- URLs not linked to any account",
                "operationId": "shortenUrl",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateUrlRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "201": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UrlResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict \u2014 resource already exists",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {},
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "JWTAuth": []
                    }
                ]
            }
        },
        "/api/v1/urls": {
            "get": {
                "tags": [
                    "Link Management"
                ],
                "summary": "List Your URLs",
                "description": "List all URLs owned by the authenticated user.\n\nReturns a paginated list of shortened URLs with support for filtering,\nsorting, and full-text search on aliases and destination URLs.\n\n**Authentication**: Required.\n\n**API Key Scope**: `urls:manage`, `urls:read`, or `admin:all`\n\n**Rate Limits**: 60/min, 5,000/day\n\n**Pagination**: Use `page` and `pageSize` query params. Response includes\n`hasNext` boolean and `total` count.\n\n**Sorting**: Sort by `created_at`, `last_click`, or `total_clicks` in\nascending or descending order.\n\n**Filtering**: Pass a JSON-encoded `filter` parameter with fields like\n`status`, `createdAfter`, `createdBefore`, `passwordSet`, `maxClicksSet`,\nand `search`.",
                "operationId": "listUrls",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "Page number (default: 1)",
                            "examples": [
                                1
                            ],
                            "default": 1,
                            "title": "Page"
                        },
                        "description": "Page number (default: 1)"
                    },
                    {
                        "name": "pageSize",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "maximum": 100,
                            "minimum": 1,
                            "description": "Items per page (default: 20, max: 100)",
                            "examples": [
                                20
                            ],
                            "default": 20,
                            "title": "Pagesize"
                        },
                        "description": "Items per page (default: 20, max: 100)"
                    },
                    {
                        "name": "sortBy",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "enum": [
                                "created_at",
                                "last_click",
                                "total_clicks"
                            ],
                            "type": "string",
                            "description": "Field to sort by",
                            "default": "created_at",
                            "title": "Sortby"
                        },
                        "description": "Field to sort by"
                    },
                    {
                        "name": "sortOrder",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "enum": [
                                "ascending",
                                "asc",
                                "1",
                                "descending",
                                "desc",
                                "-1"
                            ],
                            "type": "string",
                            "description": "Sort direction",
                            "default": "descending",
                            "title": "Sortorder"
                        },
                        "description": "Sort direction"
                    },
                    {
                        "name": "filter",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "JSON string containing filter criteria for URLs. Format: `{\"field\": value}`\n\n**Available filter fields:**\n\n- **status** \u2014 Filter by URL status (`\"ACTIVE\"` or `\"INACTIVE\"`)\n- **createdAfter** \u2014 Filter URLs created after this date (ISO 8601 datetime or Unix timestamp)\n- **createdBefore** \u2014 Filter URLs created before this date (ISO 8601 datetime or Unix timestamp)\n- **passwordSet** \u2014 Filter by password protection (boolean: `true`/`false`)\n- **maxClicksSet** \u2014 Filter by click limit presence (boolean: `true`/`false`)\n- **search** \u2014 Search in alias or long_url (case-insensitive string)\n\n**Value formats:**\n\n- **status**: String \u2014 `\"ACTIVE\"` or `\"INACTIVE\"` (case-sensitive)\n- **createdAfter / createdBefore**: ISO 8601 datetime string (e.g., `\"2024-01-01T00:00:00Z\"`) or Unix timestamp (e.g., `1704067200`)\n- **passwordSet / maxClicksSet**: Boolean \u2014 `true` or `false`\n- **search**: String \u2014 case-insensitive search term\n\n**Examples:**\n\n- `{\"status\": \"ACTIVE\"}` \u2014 Only active URLs\n- `{\"passwordSet\": true}` \u2014 Only password-protected URLs\n- `{\"createdAfter\": \"2024-01-01T00:00:00Z\"}` \u2014 URLs created after Jan 1, 2024\n- `{\"status\": \"ACTIVE\", \"maxClicksSet\": true}` \u2014 Active URLs with click limits\n- `{\"search\": \"example\"}` \u2014 URLs containing \"example\" in alias or long_url\n- `{\"createdAfter\": \"2024-01-01\", \"createdBefore\": \"2024-12-31\", \"status\": \"ACTIVE\"}` \u2014 Active URLs from 2024",
                            "examples": [
                                "{\"status\":\"ACTIVE\"}",
                                "{\"passwordSet\": true}",
                                "{\"createdAfter\": \"2024-01-01T00:00:00Z\"}",
                                "{\"status\": \"ACTIVE\", \"maxClicksSet\": true}",
                                "{\"search\": \"example\"}",
                                "{\"createdAfter\": \"2024-01-01\", \"createdBefore\": \"2024-12-31\", \"status\": \"ACTIVE\"}"
                            ],
                            "title": "Filter"
                        },
                        "description": "JSON string containing filter criteria for URLs. Format: `{\"field\": value}`\n\n**Available filter fields:**\n\n- **status** \u2014 Filter by URL status (`\"ACTIVE\"` or `\"INACTIVE\"`)\n- **createdAfter** \u2014 Filter URLs created after this date (ISO 8601 datetime or Unix timestamp)\n- **createdBefore** \u2014 Filter URLs created before this date (ISO 8601 datetime or Unix timestamp)\n- **passwordSet** \u2014 Filter by password protection (boolean: `true`/`false`)\n- **maxClicksSet** \u2014 Filter by click limit presence (boolean: `true`/`false`)\n- **search** \u2014 Search in alias or long_url (case-insensitive string)\n\n**Value formats:**\n\n- **status**: String \u2014 `\"ACTIVE\"` or `\"INACTIVE\"` (case-sensitive)\n- **createdAfter / createdBefore**: ISO 8601 datetime string (e.g., `\"2024-01-01T00:00:00Z\"`) or Unix timestamp (e.g., `1704067200`)\n- **passwordSet / maxClicksSet**: Boolean \u2014 `true` or `false`\n- **search**: String \u2014 case-insensitive search term\n\n**Examples:**\n\n- `{\"status\": \"ACTIVE\"}` \u2014 Only active URLs\n- `{\"passwordSet\": true}` \u2014 Only password-protected URLs\n- `{\"createdAfter\": \"2024-01-01T00:00:00Z\"}` \u2014 URLs created after Jan 1, 2024\n- `{\"status\": \"ACTIVE\", \"maxClicksSet\": true}` \u2014 Active URLs with click limits\n- `{\"search\": \"example\"}` \u2014 URLs containing \"example\" in alias or long_url\n- `{\"createdAfter\": \"2024-01-01\", \"createdBefore\": \"2024-12-31\", \"status\": \"ACTIVE\"}` \u2014 Active URLs from 2024"
                    },
                    {
                        "name": "filterBy",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Alias for filter parameter.",
                            "title": "Filterby"
                        },
                        "description": "Alias for filter parameter."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UrlListResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/urls/{url_id}": {
            "patch": {
                "tags": [
                    "Link Management"
                ],
                "summary": "Update URL",
                "description": "Update an existing URL's properties.\n\nPartially update a shortened URL. Only provided fields are modified; omitted\nfields remain unchanged. Pass `null` to remove optional settings like\n`password`, `max_clicks`, or `expire_after`.\n\n**Authentication**: Required \u2014 you must own the URL.\n\n**API Key Scope**: `urls:manage` or `admin:all`\n\n**Rate Limits**: 120/min, 2,000/day\n\n**Updatable Fields**: `long_url`, `alias`, `password`, `block_bots`,\n`max_clicks`, `expire_after`, `private_stats`, `status`\n\n**Notes**:\n- Setting `max_clicks` to `0` or `null` removes the click limit\n- Changing the `alias` checks availability and may fail with 409 Conflict\n- The `url_id` is the MongoDB ObjectId, not the alias",
                "operationId": "updateUrl",
                "parameters": [
                    {
                        "name": "url_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "description": "Unique identifier of the URL",
                            "title": "Url Id"
                        },
                        "description": "Unique identifier of the URL"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateUrlRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UpdateUrlResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict \u2014 resource already exists",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "tags": [
                    "Link Management"
                ],
                "summary": "Delete URL",
                "description": "Delete a URL permanently.\n\n**This action is IRREVERSIBLE.** The URL, its alias, and all associated\nclick analytics data will be permanently deleted. The alias may be reclaimed\nby another user afterward.\n\n**Authentication**: Required \u2014 you must own the URL.\n\n**API Key Scope**: `urls:manage` or `admin:all`\n\n**Rate Limits**: 60/min, 1,000/day\n\n**Recommendation**: Consider setting the URL status to `INACTIVE` via\n`PATCH /urls/{url_id}/status` instead if you may want to restore it later.",
                "operationId": "deleteUrl",
                "parameters": [
                    {
                        "name": "url_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "description": "Unique identifier of the URL",
                            "title": "Url Id"
                        },
                        "description": "Unique identifier of the URL"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeleteUrlResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/urls/{url_id}/status": {
            "patch": {
                "tags": [
                    "Link Management"
                ],
                "summary": "Update URL Status",
                "description": "Update only the status of a URL (ACTIVE / INACTIVE).\n\nToggle a URL between active and inactive without modifying other properties.\n\n**Authentication**: Required \u2014 you must own the URL.\n\n**API Key Scope**: `urls:manage` or `admin:all`\n\n**Rate Limits**: 120/min, 2,000/day\n\n**Status Values**:\n- `ACTIVE` \u2014 URL is accessible and redirects normally\n- `INACTIVE` \u2014 URL is disabled and returns an error page\n\n**Use Cases**:\n- Set `INACTIVE` to temporarily disable redirects without deleting the URL\n- Set `ACTIVE` to re-enable a previously disabled URL",
                "operationId": "updateUrlStatus",
                "parameters": [
                    {
                        "name": "url_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "description": "Unique identifier of the URL",
                            "title": "Url Id"
                        },
                        "description": "Unique identifier of the URL"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateUrlStatusRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UpdateUrlResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/stats": {
            "get": {
                "tags": [
                    "Statistics"
                ],
                "summary": "URL Statistics",
                "description": "Get click statistics for URLs.\n\nRetrieve aggregated click analytics with flexible grouping, filtering,\nand time-range options. Results are computed in a single database round-trip\nusing `$facet` aggregation.\n\n**Authentication**: Optional for `scope=anon` (public stats on a single URL);\nrequired for `scope=all` (all URLs owned by the user).\n\n**API Key Scope**: `stats:read`, `urls:read`, or `admin:all`\n\n**Rate Limits**:\n- Authenticated: 60/min, 5,000/day\n- Anonymous: 20/min, 1,000/day\n\n**Scopes**:\n- `scope=anon` + `short_code=<alias>` \u2014 public stats for one URL (if stats are not private)\n- `scope=all` \u2014 aggregate stats across all URLs owned by the authenticated user\n\n**Grouping Dimensions**: `time`, `browser`, `os`, `country`, `city`,\n`referrer`, `short_code`\n\n**Metrics**: `clicks`, `unique_clicks`\n\n**Filtering**: Filter by `browser`, `os`, `country`, `city`, `referrer`,\nor `short_code` using query params or a JSON `filters` object.",
                "operationId": "getStats",
                "parameters": [
                    {
                        "name": "scope",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "enum": [
                                "all",
                                "anon"
                            ],
                            "type": "string",
                            "description": "Statistics scope: `all` (authenticated only) or `anon` (public access).\n\n- `all` \u2014 aggregate stats across all URLs owned by the authenticated user. Requires authentication.\n- `anon` \u2014 public stats for a single URL. Requires `short_code` parameter. No authentication needed (unless stats are private).",
                            "title": "Scope"
                        },
                        "description": "Statistics scope: `all` (authenticated only) or `anon` (public access).\n\n- `all` \u2014 aggregate stats across all URLs owned by the authenticated user. Requires authentication.\n- `anon` \u2014 public stats for a single URL. Requires `short_code` parameter. No authentication needed (unless stats are private)."
                    },
                    {
                        "name": "short_code",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "URL alias to query stats for. **Required** when `scope=anon`. When `scope=all`, this is optional and filters stats to a specific URL.",
                            "examples": [
                                "mylink"
                            ],
                            "title": "Short Code"
                        },
                        "description": "URL alias to query stats for. **Required** when `scope=anon`. When `scope=all`, this is optional and filters stats to a specific URL."
                    },
                    {
                        "name": "start_date",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Start of time range. Accepts ISO 8601 datetime string (e.g., `2025-01-01T00:00:00Z`) or Unix timestamp in seconds (e.g., `1735689600`). If omitted, defaults to the URL creation date.",
                            "examples": [
                                "2025-01-01T00:00:00Z"
                            ],
                            "title": "Start Date"
                        },
                        "description": "Start of time range. Accepts ISO 8601 datetime string (e.g., `2025-01-01T00:00:00Z`) or Unix timestamp in seconds (e.g., `1735689600`). If omitted, defaults to the URL creation date."
                    },
                    {
                        "name": "end_date",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "End of time range. Accepts ISO 8601 datetime string (e.g., `2025-12-31T23:59:59Z`) or Unix timestamp in seconds (e.g., `1767225599`). If omitted, defaults to now.",
                            "examples": [
                                "2025-12-31T23:59:59Z"
                            ],
                            "title": "End Date"
                        },
                        "description": "End of time range. Accepts ISO 8601 datetime string (e.g., `2025-12-31T23:59:59Z`) or Unix timestamp in seconds (e.g., `1767225599`). If omitted, defaults to now."
                    },
                    {
                        "name": "group_by",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Comma-separated grouping dimensions for the statistics breakdown. Defaults to `time` if omitted.\n\n**Available dimensions:**\n\n- `time` \u2014 group by time buckets (day/week/month, auto-selected based on range)\n- `browser` \u2014 group by browser name (e.g., Chrome, Firefox, Safari)\n- `os` \u2014 group by operating system (e.g., Windows, macOS, Linux)\n- `country` \u2014 group by country\n- `city` \u2014 group by city\n- `referrer` \u2014 group by referrer URL\n- `short_code` \u2014 group by URL alias (only with `scope=all`)\n\nMultiple dimensions can be combined: `time,browser` returns time series broken down by browser.",
                            "examples": [
                                "time,browser",
                                "country",
                                "time,country,browser"
                            ],
                            "title": "Group By"
                        },
                        "description": "Comma-separated grouping dimensions for the statistics breakdown. Defaults to `time` if omitted.\n\n**Available dimensions:**\n\n- `time` \u2014 group by time buckets (day/week/month, auto-selected based on range)\n- `browser` \u2014 group by browser name (e.g., Chrome, Firefox, Safari)\n- `os` \u2014 group by operating system (e.g., Windows, macOS, Linux)\n- `country` \u2014 group by country\n- `city` \u2014 group by city\n- `referrer` \u2014 group by referrer URL\n- `short_code` \u2014 group by URL alias (only with `scope=all`)\n\nMultiple dimensions can be combined: `time,browser` returns time series broken down by browser."
                    },
                    {
                        "name": "metrics",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Comma-separated metrics to include. Defaults to `clicks,unique_clicks` if omitted.\n\n**Available metrics:**\n\n- `clicks` \u2014 total click count\n- `unique_clicks` \u2014 unique visitor count (deduplicated by IP + User-Agent)",
                            "examples": [
                                "clicks,unique_clicks",
                                "clicks"
                            ],
                            "title": "Metrics"
                        },
                        "description": "Comma-separated metrics to include. Defaults to `clicks,unique_clicks` if omitted.\n\n**Available metrics:**\n\n- `clicks` \u2014 total click count\n- `unique_clicks` \u2014 unique visitor count (deduplicated by IP + User-Agent)"
                    },
                    {
                        "name": "timezone",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "description": "IANA timezone name for time-based grouping and output formatting (e.g., `UTC`, `America/New_York`, `Asia/Kolkata`). Defaults to `UTC`.",
                            "examples": [
                                "UTC",
                                "America/New_York"
                            ],
                            "default": "UTC",
                            "title": "Timezone"
                        },
                        "description": "IANA timezone name for time-based grouping and output formatting (e.g., `UTC`, `America/New_York`, `Asia/Kolkata`). Defaults to `UTC`."
                    },
                    {
                        "name": "filters",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 1: JSON Filters Object**\n\nJSON string containing dimension filters. Format: `{\"dimension\": [\"value1\", \"value2\"]}`\n\n**Available filter dimensions:**\n\n- `browser` \u2014 Filter by browser name (e.g., Chrome, Firefox, Safari, Edge)\n- `os` \u2014 Filter by operating system (e.g., Windows, macOS, Linux, iOS, Android)\n- `country` \u2014 Filter by country name (e.g., United States, Canada, Germany)\n- `city` \u2014 Filter by city name (e.g., New York, London, Mumbai)\n- `referrer` \u2014 Filter by referrer URL (e.g., https://google.com, https://twitter.com)\n- `short_code` \u2014 Filter by URL alias (e.g., mylink, promo2024) \u2014 **not allowed** with `scope=anon`\n\n**Value format:** Array of strings for each dimension.\n\n**Important:** Filter values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Examples:**\n\n- `{\"browser\": [\"Chrome\", \"Firefox\"]}` \u2014 Chrome OR Firefox clicks\n- `{\"country\": [\"United States\", \"Canada\"], \"browser\": [\"Chrome\"]}` \u2014 US/CA clicks from Chrome\n- `{\"short_code\": [\"link1\", \"link2\"]}` \u2014 Stats for specific URLs (`scope=all` only)\n\n**Alternative:** You can also pass filters as individual query parameters (see `browser`, `os`, `country`, `city`, `referrer` parameters below).",
                            "examples": [
                                "{\"browser\":[\"Chrome\",\"Firefox\"]}",
                                "{\"country\":[\"United States\",\"Canada\"],\"browser\":[\"Chrome\"]}"
                            ],
                            "title": "Filters"
                        },
                        "description": "**Method 1: JSON Filters Object**\n\nJSON string containing dimension filters. Format: `{\"dimension\": [\"value1\", \"value2\"]}`\n\n**Available filter dimensions:**\n\n- `browser` \u2014 Filter by browser name (e.g., Chrome, Firefox, Safari, Edge)\n- `os` \u2014 Filter by operating system (e.g., Windows, macOS, Linux, iOS, Android)\n- `country` \u2014 Filter by country name (e.g., United States, Canada, Germany)\n- `city` \u2014 Filter by city name (e.g., New York, London, Mumbai)\n- `referrer` \u2014 Filter by referrer URL (e.g., https://google.com, https://twitter.com)\n- `short_code` \u2014 Filter by URL alias (e.g., mylink, promo2024) \u2014 **not allowed** with `scope=anon`\n\n**Value format:** Array of strings for each dimension.\n\n**Important:** Filter values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Examples:**\n\n- `{\"browser\": [\"Chrome\", \"Firefox\"]}` \u2014 Chrome OR Firefox clicks\n- `{\"country\": [\"United States\", \"Canada\"], \"browser\": [\"Chrome\"]}` \u2014 US/CA clicks from Chrome\n- `{\"short_code\": [\"link1\", \"link2\"]}` \u2014 Stats for specific URLs (`scope=all` only)\n\n**Alternative:** You can also pass filters as individual query parameters (see `browser`, `os`, `country`, `city`, `referrer` parameters below)."
                    },
                    {
                        "name": "browser",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated browser names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Chrome, Firefox, Safari, Edge, Opera, Samsung Internet.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "Chrome,Firefox"
                            ],
                            "title": "Browser"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated browser names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Chrome, Firefox, Safari, Edge, Opera, Samsung Internet.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "os",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated operating system names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Windows, macOS, Linux, iOS, Android, Chrome OS.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "Windows,macOS"
                            ],
                            "title": "Os"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated operating system names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Windows, macOS, Linux, iOS, Android, Chrome OS.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "country",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated country names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use full country names as stored in the database (e.g., United States, Canada, United Kingdom, India, Germany, France, Japan).\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "United States,Germany"
                            ],
                            "title": "Country"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated country names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use full country names as stored in the database (e.g., United States, Canada, United Kingdom, India, Germany, France, Japan).\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "city",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated city names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "San Francisco,Berlin"
                            ],
                            "title": "City"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated city names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "referrer",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated referrer URLs. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Include the full URL including protocol.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "https://google.com,https://twitter.com"
                            ],
                            "title": "Referrer"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated referrer URLs. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Include the full URL including protocol.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/StatsResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {},
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "JWTAuth": []
                    }
                ]
            }
        },
        "/api/v1/export": {
            "get": {
                "tags": [
                    "Statistics"
                ],
                "summary": "Export Statistics",
                "description": "Export URL click statistics as a downloadable file.\n\nGenerate a file export of click analytics data in the specified format.\nThe response is a binary download with appropriate `Content-Disposition` header.\n\n**Authentication**: Optional for `scope=anon` (public stats on a single URL);\nrequired for `scope=all`.\n\n**API Key Scope**: `stats:read`, `urls:read`, or `admin:all`\n\n**Rate Limits**:\n- Authenticated: 30/min, 1,000/day\n- Anonymous: 10/min, 200/day\n\n**Export Formats**:\n- `json` \u2014 single JSON file\n- `xml` \u2014 single XML file\n- `xlsx` \u2014 Excel spreadsheet with multiple sheets\n- `csv` \u2014 **ZIP archive** containing `summary.csv` plus one CSV file per metrics dimension\n\n**Note**: Export generation is resource-intensive. Lower rate limits apply\ncompared to other endpoints.",
                "operationId": "exportStats",
                "parameters": [
                    {
                        "name": "scope",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "enum": [
                                "all",
                                "anon"
                            ],
                            "type": "string",
                            "description": "Statistics scope: `all` (authenticated only) or `anon` (public access).\n\n- `all` \u2014 aggregate stats across all URLs owned by the authenticated user. Requires authentication.\n- `anon` \u2014 public stats for a single URL. Requires `short_code` parameter. No authentication needed (unless stats are private).",
                            "title": "Scope"
                        },
                        "description": "Statistics scope: `all` (authenticated only) or `anon` (public access).\n\n- `all` \u2014 aggregate stats across all URLs owned by the authenticated user. Requires authentication.\n- `anon` \u2014 public stats for a single URL. Requires `short_code` parameter. No authentication needed (unless stats are private)."
                    },
                    {
                        "name": "short_code",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "URL alias to query stats for. **Required** when `scope=anon`. When `scope=all`, this is optional and filters stats to a specific URL.",
                            "examples": [
                                "mylink"
                            ],
                            "title": "Short Code"
                        },
                        "description": "URL alias to query stats for. **Required** when `scope=anon`. When `scope=all`, this is optional and filters stats to a specific URL."
                    },
                    {
                        "name": "start_date",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Start of time range. Accepts ISO 8601 datetime string (e.g., `2025-01-01T00:00:00Z`) or Unix timestamp in seconds (e.g., `1735689600`). If omitted, defaults to the URL creation date.",
                            "examples": [
                                "2025-01-01T00:00:00Z"
                            ],
                            "title": "Start Date"
                        },
                        "description": "Start of time range. Accepts ISO 8601 datetime string (e.g., `2025-01-01T00:00:00Z`) or Unix timestamp in seconds (e.g., `1735689600`). If omitted, defaults to the URL creation date."
                    },
                    {
                        "name": "end_date",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "End of time range. Accepts ISO 8601 datetime string (e.g., `2025-12-31T23:59:59Z`) or Unix timestamp in seconds (e.g., `1767225599`). If omitted, defaults to now.",
                            "examples": [
                                "2025-12-31T23:59:59Z"
                            ],
                            "title": "End Date"
                        },
                        "description": "End of time range. Accepts ISO 8601 datetime string (e.g., `2025-12-31T23:59:59Z`) or Unix timestamp in seconds (e.g., `1767225599`). If omitted, defaults to now."
                    },
                    {
                        "name": "group_by",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Comma-separated grouping dimensions for the statistics breakdown. Defaults to `time` if omitted.\n\n**Available dimensions:**\n\n- `time` \u2014 group by time buckets (day/week/month, auto-selected based on range)\n- `browser` \u2014 group by browser name (e.g., Chrome, Firefox, Safari)\n- `os` \u2014 group by operating system (e.g., Windows, macOS, Linux)\n- `country` \u2014 group by country\n- `city` \u2014 group by city\n- `referrer` \u2014 group by referrer URL\n- `short_code` \u2014 group by URL alias (only with `scope=all`)\n\nMultiple dimensions can be combined: `time,browser` returns time series broken down by browser.",
                            "examples": [
                                "time,browser",
                                "country",
                                "time,country,browser"
                            ],
                            "title": "Group By"
                        },
                        "description": "Comma-separated grouping dimensions for the statistics breakdown. Defaults to `time` if omitted.\n\n**Available dimensions:**\n\n- `time` \u2014 group by time buckets (day/week/month, auto-selected based on range)\n- `browser` \u2014 group by browser name (e.g., Chrome, Firefox, Safari)\n- `os` \u2014 group by operating system (e.g., Windows, macOS, Linux)\n- `country` \u2014 group by country\n- `city` \u2014 group by city\n- `referrer` \u2014 group by referrer URL\n- `short_code` \u2014 group by URL alias (only with `scope=all`)\n\nMultiple dimensions can be combined: `time,browser` returns time series broken down by browser."
                    },
                    {
                        "name": "metrics",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "Comma-separated metrics to include. Defaults to `clicks,unique_clicks` if omitted.\n\n**Available metrics:**\n\n- `clicks` \u2014 total click count\n- `unique_clicks` \u2014 unique visitor count (deduplicated by IP + User-Agent)",
                            "examples": [
                                "clicks,unique_clicks",
                                "clicks"
                            ],
                            "title": "Metrics"
                        },
                        "description": "Comma-separated metrics to include. Defaults to `clicks,unique_clicks` if omitted.\n\n**Available metrics:**\n\n- `clicks` \u2014 total click count\n- `unique_clicks` \u2014 unique visitor count (deduplicated by IP + User-Agent)"
                    },
                    {
                        "name": "timezone",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "description": "IANA timezone name for time-based grouping and output formatting (e.g., `UTC`, `America/New_York`, `Asia/Kolkata`). Defaults to `UTC`.",
                            "examples": [
                                "UTC",
                                "America/New_York"
                            ],
                            "default": "UTC",
                            "title": "Timezone"
                        },
                        "description": "IANA timezone name for time-based grouping and output formatting (e.g., `UTC`, `America/New_York`, `Asia/Kolkata`). Defaults to `UTC`."
                    },
                    {
                        "name": "filters",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 1: JSON Filters Object**\n\nJSON string containing dimension filters. Format: `{\"dimension\": [\"value1\", \"value2\"]}`\n\n**Available filter dimensions:**\n\n- `browser` \u2014 Filter by browser name (e.g., Chrome, Firefox, Safari, Edge)\n- `os` \u2014 Filter by operating system (e.g., Windows, macOS, Linux, iOS, Android)\n- `country` \u2014 Filter by country name (e.g., United States, Canada, Germany)\n- `city` \u2014 Filter by city name (e.g., New York, London, Mumbai)\n- `referrer` \u2014 Filter by referrer URL (e.g., https://google.com, https://twitter.com)\n- `short_code` \u2014 Filter by URL alias (e.g., mylink, promo2024) \u2014 **not allowed** with `scope=anon`\n\n**Value format:** Array of strings for each dimension.\n\n**Important:** Filter values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Examples:**\n\n- `{\"browser\": [\"Chrome\", \"Firefox\"]}` \u2014 Chrome OR Firefox clicks\n- `{\"country\": [\"United States\", \"Canada\"], \"browser\": [\"Chrome\"]}` \u2014 US/CA clicks from Chrome\n- `{\"short_code\": [\"link1\", \"link2\"]}` \u2014 Stats for specific URLs (`scope=all` only)\n\n**Alternative:** You can also pass filters as individual query parameters (see `browser`, `os`, `country`, `city`, `referrer` parameters below).",
                            "examples": [
                                "{\"browser\":[\"Chrome\",\"Firefox\"]}",
                                "{\"country\":[\"United States\",\"Canada\"],\"browser\":[\"Chrome\"]}"
                            ],
                            "title": "Filters"
                        },
                        "description": "**Method 1: JSON Filters Object**\n\nJSON string containing dimension filters. Format: `{\"dimension\": [\"value1\", \"value2\"]}`\n\n**Available filter dimensions:**\n\n- `browser` \u2014 Filter by browser name (e.g., Chrome, Firefox, Safari, Edge)\n- `os` \u2014 Filter by operating system (e.g., Windows, macOS, Linux, iOS, Android)\n- `country` \u2014 Filter by country name (e.g., United States, Canada, Germany)\n- `city` \u2014 Filter by city name (e.g., New York, London, Mumbai)\n- `referrer` \u2014 Filter by referrer URL (e.g., https://google.com, https://twitter.com)\n- `short_code` \u2014 Filter by URL alias (e.g., mylink, promo2024) \u2014 **not allowed** with `scope=anon`\n\n**Value format:** Array of strings for each dimension.\n\n**Important:** Filter values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Examples:**\n\n- `{\"browser\": [\"Chrome\", \"Firefox\"]}` \u2014 Chrome OR Firefox clicks\n- `{\"country\": [\"United States\", \"Canada\"], \"browser\": [\"Chrome\"]}` \u2014 US/CA clicks from Chrome\n- `{\"short_code\": [\"link1\", \"link2\"]}` \u2014 Stats for specific URLs (`scope=all` only)\n\n**Alternative:** You can also pass filters as individual query parameters (see `browser`, `os`, `country`, `city`, `referrer` parameters below)."
                    },
                    {
                        "name": "browser",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated browser names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Chrome, Firefox, Safari, Edge, Opera, Samsung Internet.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "Chrome,Firefox"
                            ],
                            "title": "Browser"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated browser names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Chrome, Firefox, Safari, Edge, Opera, Samsung Internet.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "os",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated operating system names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Windows, macOS, Linux, iOS, Android, Chrome OS.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "Windows,macOS"
                            ],
                            "title": "Os"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated operating system names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Common values include: Windows, macOS, Linux, iOS, Android, Chrome OS.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "country",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated country names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use full country names as stored in the database (e.g., United States, Canada, United Kingdom, India, Germany, France, Japan).\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "United States,Germany"
                            ],
                            "title": "Country"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated country names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use full country names as stored in the database (e.g., United States, Canada, United Kingdom, India, Germany, France, Japan).\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "city",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated city names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "San Francisco,Berlin"
                            ],
                            "title": "City"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated city names. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Use exact capitalization as stored in the database.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "referrer",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "null"
                                }
                            ],
                            "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated referrer URLs. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Include the full URL including protocol.\n\n**Note:** Both `filters` JSON and individual parameters can be combined.",
                            "examples": [
                                "https://google.com,https://twitter.com"
                            ],
                            "title": "Referrer"
                        },
                        "description": "**Method 2: Individual Filter Parameter**\n\nComma-separated referrer URLs. Alternative to using the `filters` JSON parameter.\n\n**Important:** Values are case-sensitive. Include the full URL including protocol.\n\n**Note:** Both `filters` JSON and individual parameters can be combined."
                    },
                    {
                        "name": "format",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "enum": [
                                "csv",
                                "xlsx",
                                "json",
                                "xml"
                            ],
                            "type": "string",
                            "description": "Export file format.",
                            "title": "Format"
                        },
                        "description": "Export file format."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Export file download",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/xml": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                },
                                "x-description": "CSV export \u2014 ZIP archive containing summary.csv plus one file per dimension"
                            },
                            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                },
                                "x-description": "XLSX export \u2014 Excel workbook with multiple sheets"
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "Internal server error \u2014 export generation failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {},
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "JWTAuth": []
                    }
                ]
            }
        },
        "/api/v1/keys": {
            "get": {
                "tags": [
                    "API Keys"
                ],
                "summary": "List API Keys",
                "description": "List all API keys for the authenticated user.\n\nReturns metadata for all API keys (both active and revoked) belonging to the\nauthenticated user. The full token value is **never** returned in this\nendpoint for security reasons \u2014 only the `token_prefix` is shown.\n\n**Authentication**: Required \u2014 JWT Bearer or API key with appropriate scope.\n\n**Rate Limits**: 60/min",
                "operationId": "listApiKeys",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiKeysListResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "API Keys"
                ],
                "summary": "Create API Key",
                "description": "Create a new API key for programmatic access.\n\nGenerate a new API key with the specified name and scopes. The full token\n(prefixed with `spoo_`) is returned **only in this response** and cannot be\nretrieved again.\n\n**Authentication**: Required \u2014 JWT Bearer only (API keys cannot create other\nAPI keys). Email must be verified.\n\n**Rate Limits**: 5/hour\n\n**Available Scopes**: `shorten:create`, `urls:manage`, `urls:read`,\n`stats:read`, `admin:all`\n\n**Notes**:\n- Store the returned `token` securely \u2014 it will not be shown again\n- Set `expires_at` to limit the key's lifetime (ISO 8601 or Unix epoch)\n- Omit `expires_at` for a non-expiring key",
                "operationId": "createApiKey",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateApiKeyRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "201": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiKeyCreatedResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Conflict \u2014 resource already exists",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/keys/{key_id}": {
            "delete": {
                "tags": [
                    "API Keys"
                ],
                "summary": "Delete/Revoke API Key",
                "description": "Delete or revoke an API key.\n\nRemove an API key either permanently (hard delete) or by marking it as\nrevoked (soft delete). Revoked keys stop working immediately but remain\nvisible in the key list for audit purposes.\n\n**Authentication**: Required \u2014 JWT Bearer or API key.\n\n**Rate Limits**: 30/min\n\n**Modes**:\n- `?revoke=false` (default) \u2014 **permanently deletes** the key record\n- `?revoke=true` \u2014 marks the key as revoked but preserves the record;\n  the key appears with `revoked: true` in the list endpoint",
                "operationId": "deleteApiKey",
                "parameters": [
                    {
                        "name": "key_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Key Id"
                        }
                    },
                    {
                        "name": "revoke",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "boolean",
                            "default": false,
                            "title": "Revoke"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApiKeyActionResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Bad Request \u2014 invalid parameters",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized \u2014 missing or invalid credentials",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Forbidden \u2014 insufficient permissions or scope",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ErrorResponse"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "ApiKeyActionResponse": {
                "properties": {
                    "success": {
                        "type": "boolean",
                        "title": "Success",
                        "description": "Whether the action completed successfully"
                    },
                    "action": {
                        "type": "string",
                        "title": "Action",
                        "description": "Action that was performed",
                        "examples": [
                            "deleted"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "success",
                    "action"
                ],
                "title": "ApiKeyActionResponse",
                "description": "Response body for DELETE /api/v1/keys/{key_id}."
            },
            "ApiKeyCreatedResponse": {
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "Id",
                        "description": "API key ID",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    },
                    "name": {
                        "type": "string",
                        "title": "Name",
                        "description": "Human-readable key name",
                        "examples": [
                            "My Production Key"
                        ]
                    },
                    "description": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Description",
                        "description": "Optional description",
                        "examples": [
                            "Used by the mobile app"
                        ]
                    },
                    "scopes": {
                        "items": {
                            "type": "string"
                        },
                        "type": "array",
                        "title": "Scopes",
                        "description": "Permission scopes granted to this key",
                        "examples": [
                            [
                                "shorten:create",
                                "stats:read"
                            ]
                        ]
                    },
                    "created_at": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Created At",
                        "description": "Creation time as Unix timestamp",
                        "examples": [
                            1704067200
                        ]
                    },
                    "expires_at": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expires At",
                        "description": "Expiration time as Unix timestamp, or null if no expiration",
                        "examples": [
                            1735689600
                        ]
                    },
                    "revoked": {
                        "type": "boolean",
                        "title": "Revoked",
                        "description": "Whether the key has been revoked"
                    },
                    "token_prefix": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Token Prefix",
                        "description": "First characters of the token for identification",
                        "examples": [
                            "spoo_abc1"
                        ]
                    },
                    "token": {
                        "type": "string",
                        "title": "Token",
                        "description": "Full API key token (only returned once at creation time)",
                        "examples": [
                            "spoo_abc123def456ghi789"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "id",
                    "name",
                    "scopes",
                    "revoked",
                    "token"
                ],
                "title": "ApiKeyCreatedResponse",
                "description": "Response for POST /api/v1/keys (201).\n\nExtends ApiKeyResponse by adding the full ``token``.  This is the ONLY time\nthe token is returned \u2014 it is hashed before storage."
            },
            "ApiKeyResponse": {
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "Id",
                        "description": "API key ID",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    },
                    "name": {
                        "type": "string",
                        "title": "Name",
                        "description": "Human-readable key name",
                        "examples": [
                            "My Production Key"
                        ]
                    },
                    "description": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Description",
                        "description": "Optional description",
                        "examples": [
                            "Used by the mobile app"
                        ]
                    },
                    "scopes": {
                        "items": {
                            "type": "string"
                        },
                        "type": "array",
                        "title": "Scopes",
                        "description": "Permission scopes granted to this key",
                        "examples": [
                            [
                                "shorten:create",
                                "stats:read"
                            ]
                        ]
                    },
                    "created_at": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Created At",
                        "description": "Creation time as Unix timestamp",
                        "examples": [
                            1704067200
                        ]
                    },
                    "expires_at": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expires At",
                        "description": "Expiration time as Unix timestamp, or null if no expiration",
                        "examples": [
                            1735689600
                        ]
                    },
                    "revoked": {
                        "type": "boolean",
                        "title": "Revoked",
                        "description": "Whether the key has been revoked"
                    },
                    "token_prefix": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Token Prefix",
                        "description": "First characters of the token for identification",
                        "examples": [
                            "spoo_abc1"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "id",
                    "name",
                    "scopes",
                    "revoked"
                ],
                "title": "ApiKeyResponse",
                "description": "A single API key entry as returned by the list endpoint.\n\nThe full token is never returned here \u2014 only the ``token_prefix`` for display."
            },
            "ApiKeysListResponse": {
                "properties": {
                    "keys": {
                        "items": {
                            "$ref": "#/components/schemas/ApiKeyResponse"
                        },
                        "type": "array",
                        "title": "Keys",
                        "description": "List of API keys for the authenticated user"
                    }
                },
                "type": "object",
                "required": [
                    "keys"
                ],
                "title": "ApiKeysListResponse",
                "description": "Response body for GET /api/v1/keys."
            },
            "AuthProviderInfo": {
                "properties": {
                    "provider": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Provider",
                        "description": "OAuth provider name",
                        "examples": [
                            "google"
                        ]
                    },
                    "email": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Email",
                        "description": "Email address from the OAuth provider",
                        "examples": [
                            "user@gmail.com"
                        ]
                    },
                    "linked_at": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Linked At",
                        "description": "ISO 8601 timestamp when the provider was linked",
                        "examples": [
                            "2025-01-15T10:30:00+00:00"
                        ]
                    }
                },
                "type": "object",
                "title": "AuthProviderInfo",
                "description": "Minimal OAuth provider entry returned inside UserProfileResponse."
            },
            "ComputedMetrics": {
                "properties": {
                    "unique_click_rate": {
                        "type": "number",
                        "title": "Unique Click Rate"
                    },
                    "repeat_click_rate": {
                        "type": "number",
                        "title": "Repeat Click Rate"
                    },
                    "average_clicks_per_visitor": {
                        "type": "number",
                        "title": "Average Clicks Per Visitor"
                    }
                },
                "type": "object",
                "required": [
                    "unique_click_rate",
                    "repeat_click_rate",
                    "average_clicks_per_visitor"
                ],
                "title": "ComputedMetrics",
                "description": "Optional computed metrics added by format_stats_response_with_metadata."
            },
            "CreateApiKeyRequest": {
                "properties": {
                    "name": {
                        "type": "string",
                        "title": "Name",
                        "description": "Human-readable key name",
                        "examples": [
                            "My Production Key"
                        ]
                    },
                    "description": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Description",
                        "description": "Optional description of what this key is used for",
                        "examples": [
                            "Used by the mobile app for URL shortening"
                        ]
                    },
                    "scopes": {
                        "items": {
                            "type": "string"
                        },
                        "type": "array",
                        "title": "Scopes",
                        "description": "Permission scopes for the key",
                        "examples": [
                            [
                                "shorten:create",
                                "stats:read"
                            ]
                        ]
                    },
                    "expires_at": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expires At",
                        "description": "Expiration time. ISO 8601 string (e.g. `2026-01-01T00:00:00Z`) or Unix epoch seconds (e.g. `1735689599`). Omit for non-expiring key.",
                        "examples": [
                            "2026-01-01T00:00:00Z",
                            1735689599
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "name",
                    "scopes"
                ],
                "title": "CreateApiKeyRequest",
                "description": "Request body for POST /api/v1/keys."
            },
            "CreateUrlRequest": {
                "properties": {
                    "long_url": {
                        "type": "string",
                        "title": "Long Url",
                        "description": "The destination URL to shorten. Must be a valid http:// or https:// URL.",
                        "examples": [
                            "https://example.com/very/long/url/path"
                        ]
                    },
                    "alias": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Alias",
                        "description": "Custom short code. Alphanumeric, hyphens, underscores. 3-16 chars. Auto-generated if omitted.",
                        "examples": [
                            "mylink"
                        ]
                    },
                    "password": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Password",
                        "description": "Password to protect the URL. Min 8 chars, must contain letter + number + special char.",
                        "examples": [
                            "secure@123"
                        ]
                    },
                    "block_bots": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Block Bots",
                        "description": "Block known bot user agents from accessing the URL."
                    },
                    "max_clicks": {
                        "anyOf": [
                            {
                                "type": "integer",
                                "exclusiveMinimum": 0.0
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Max Clicks",
                        "description": "Maximum clicks before the URL expires. Must be positive.",
                        "examples": [
                            100
                        ]
                    },
                    "expire_after": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expire After",
                        "description": "Expiration time. ISO 8601 string (e.g. `2025-12-31T23:59:59Z`) or Unix epoch seconds (e.g. `1735689599`).",
                        "examples": [
                            "2025-12-31T23:59:59Z",
                            1735689599
                        ]
                    },
                    "private_stats": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Private Stats",
                        "description": "Make statistics private (only owner can view). Requires authentication."
                    }
                },
                "type": "object",
                "required": [
                    "long_url"
                ],
                "title": "CreateUrlRequest",
                "description": "Request body for creating a new shortened URL.\n\nAccepts ``url`` as an alias for ``long_url`` \u2014 the existing API supports both."
            },
            "DeleteUrlResponse": {
                "properties": {
                    "message": {
                        "type": "string",
                        "title": "Message",
                        "description": "Confirmation message.",
                        "examples": [
                            "URL deleted"
                        ]
                    },
                    "id": {
                        "type": "string",
                        "title": "Id",
                        "description": "ID of the deleted URL.",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "message",
                    "id"
                ],
                "title": "DeleteUrlResponse",
                "description": "Response body for DELETE /api/v1/urls/{url_id}."
            },
            "ErrorResponse": {
                "properties": {
                    "error": {
                        "type": "string",
                        "title": "Error"
                    },
                    "error_code": {
                        "type": "string",
                        "title": "Error Code"
                    },
                    "field": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Field"
                    },
                    "details": {
                        "anyOf": [
                            {},
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Details"
                    }
                },
                "type": "object",
                "required": [
                    "error",
                    "error_code"
                ],
                "title": "ErrorResponse",
                "description": "Standard error JSON body produced by the AppError exception handler."
            },
            "HTTPValidationError": {
                "properties": {
                    "detail": {
                        "items": {
                            "$ref": "#/components/schemas/ValidationError"
                        },
                        "type": "array",
                        "title": "Detail"
                    }
                },
                "type": "object",
                "title": "HTTPValidationError"
            },
            "LoginRequest": {
                "properties": {
                    "email": {
                        "type": "string",
                        "title": "Email",
                        "description": "Account email address",
                        "examples": [
                            "user@example.com"
                        ]
                    },
                    "password": {
                        "type": "string",
                        "title": "Password",
                        "description": "Account password",
                        "examples": [
                            "MySecurePass123!"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "email",
                    "password"
                ],
                "title": "LoginRequest",
                "description": "Request body for POST /auth/login."
            },
            "LoginResponse": {
                "properties": {
                    "access_token": {
                        "type": "string",
                        "title": "Access Token",
                        "description": "JWT access token",
                        "examples": [
                            "eyJhbGciOiJIUzI1NiIs..."
                        ]
                    },
                    "user": {
                        "$ref": "#/components/schemas/UserProfileResponse",
                        "description": "Authenticated user's profile"
                    }
                },
                "type": "object",
                "required": [
                    "access_token",
                    "user"
                ],
                "title": "LoginResponse",
                "description": "Response body for POST /auth/login (200)."
            },
            "LogoutResponse": {
                "properties": {
                    "success": {
                        "type": "boolean",
                        "title": "Success",
                        "description": "Always true on successful logout"
                    }
                },
                "type": "object",
                "required": [
                    "success"
                ],
                "title": "LogoutResponse",
                "description": "Response body for POST /auth/logout (200)."
            },
            "MeResponse": {
                "properties": {
                    "user": {
                        "$ref": "#/components/schemas/UserProfileResponse",
                        "description": "Current authenticated user's profile"
                    }
                },
                "type": "object",
                "required": [
                    "user"
                ],
                "title": "MeResponse",
                "description": "Response body for GET /auth/me (200)."
            },
            "MessageResponse": {
                "properties": {
                    "success": {
                        "type": "boolean",
                        "title": "Success"
                    },
                    "message": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Message"
                    }
                },
                "type": "object",
                "required": [
                    "success"
                ],
                "title": "MessageResponse",
                "description": "Generic success/message response returned by several endpoints."
            },
            "OAuthProvidersResponse": {
                "properties": {
                    "providers": {
                        "items": {
                            "additionalProperties": true,
                            "type": "object"
                        },
                        "type": "array",
                        "title": "Providers",
                        "description": "List of linked OAuth providers with name, email, and linked_at"
                    },
                    "password_set": {
                        "type": "boolean",
                        "title": "Password Set",
                        "description": "Whether the user has a password set (affects unlink eligibility)"
                    }
                },
                "type": "object",
                "required": [
                    "providers",
                    "password_set"
                ],
                "title": "OAuthProvidersResponse",
                "description": "Response body for GET /oauth/providers (200)."
            },
            "RefreshResponse": {
                "properties": {
                    "access_token": {
                        "type": "string",
                        "title": "Access Token",
                        "description": "New JWT access token",
                        "examples": [
                            "eyJhbGciOiJIUzI1NiIs..."
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "access_token"
                ],
                "title": "RefreshResponse",
                "description": "Response body for POST /auth/refresh (200)."
            },
            "RegisterRequest": {
                "properties": {
                    "email": {
                        "type": "string",
                        "title": "Email",
                        "description": "Email address for the new account",
                        "examples": [
                            "newuser@example.com"
                        ]
                    },
                    "password": {
                        "type": "string",
                        "title": "Password",
                        "description": "Password (min 8 chars, must contain letter + number + special char)",
                        "examples": [
                            "MySecurePass123!"
                        ]
                    },
                    "user_name": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "User Name",
                        "description": "Display name (optional)",
                        "examples": [
                            "Jane Doe"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "email",
                    "password"
                ],
                "title": "RegisterRequest",
                "description": "Request body for POST /auth/register."
            },
            "RegisterResponse": {
                "properties": {
                    "access_token": {
                        "type": "string",
                        "title": "Access Token",
                        "description": "JWT access token",
                        "examples": [
                            "eyJhbGciOiJIUzI1NiIs..."
                        ]
                    },
                    "user": {
                        "$ref": "#/components/schemas/UserProfileResponse",
                        "description": "Newly created user's profile"
                    },
                    "requires_verification": {
                        "type": "boolean",
                        "title": "Requires Verification",
                        "description": "Whether email verification is required before accessing protected resources"
                    },
                    "verification_sent": {
                        "type": "boolean",
                        "title": "Verification Sent",
                        "description": "Whether the verification email was sent successfully"
                    }
                },
                "type": "object",
                "required": [
                    "access_token",
                    "user",
                    "requires_verification",
                    "verification_sent"
                ],
                "title": "RegisterResponse",
                "description": "Response body for POST /auth/register (201)."
            },
            "RequestPasswordResetRequest": {
                "properties": {
                    "email": {
                        "type": "string",
                        "title": "Email",
                        "description": "Email address of the account to reset",
                        "examples": [
                            "user@example.com"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "email"
                ],
                "title": "RequestPasswordResetRequest",
                "description": "Request body for POST /auth/request-password-reset."
            },
            "ResetPasswordRequest": {
                "properties": {
                    "email": {
                        "type": "string",
                        "title": "Email",
                        "description": "Email address of the account",
                        "examples": [
                            "user@example.com"
                        ]
                    },
                    "code": {
                        "type": "string",
                        "title": "Code",
                        "description": "6-digit OTP from password reset email",
                        "examples": [
                            "123456"
                        ]
                    },
                    "password": {
                        "type": "string",
                        "title": "Password",
                        "description": "New password (min 8 chars, must contain letter + number + special char)",
                        "examples": [
                            "NewSecurePass456!"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "email",
                    "code",
                    "password"
                ],
                "title": "ResetPasswordRequest",
                "description": "Request body for POST /auth/reset-password."
            },
            "SendVerificationResponse": {
                "properties": {
                    "success": {
                        "type": "boolean",
                        "title": "Success",
                        "description": "Whether the verification email was sent"
                    },
                    "message": {
                        "type": "string",
                        "title": "Message",
                        "description": "Human-readable status message"
                    },
                    "expires_in": {
                        "type": "integer",
                        "title": "Expires In",
                        "description": "OTP expiry duration in seconds",
                        "examples": [
                            600
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "success",
                    "message",
                    "expires_in"
                ],
                "title": "SendVerificationResponse",
                "description": "Response body for POST /auth/send-verification (200)."
            },
            "SetPasswordRequest": {
                "properties": {
                    "password": {
                        "type": "string",
                        "title": "Password",
                        "description": "New password (min 8 chars, must contain letter + number + special char)",
                        "examples": [
                            "MySecurePass123!"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "password"
                ],
                "title": "SetPasswordRequest",
                "description": "Request body for POST /auth/set-password.\n\nOnly applies to OAuth-only users who have not yet set a password."
            },
            "StatsResponse": {
                "properties": {
                    "scope": {
                        "type": "string",
                        "title": "Scope"
                    },
                    "filters": {
                        "additionalProperties": true,
                        "type": "object",
                        "title": "Filters"
                    },
                    "group_by": {
                        "items": {
                            "type": "string"
                        },
                        "type": "array",
                        "title": "Group By"
                    },
                    "timezone": {
                        "type": "string",
                        "title": "Timezone"
                    },
                    "time_range": {
                        "$ref": "#/components/schemas/StatsTimeRange"
                    },
                    "summary": {
                        "$ref": "#/components/schemas/StatsSummary"
                    },
                    "metrics": {
                        "additionalProperties": {
                            "items": {
                                "additionalProperties": true,
                                "type": "object"
                            },
                            "type": "array"
                        },
                        "type": "object",
                        "title": "Metrics",
                        "description": "Keyed by '{metric}_by_{dimension}' (e.g. 'clicks_by_browser', 'unique_clicks_by_time'). Each value is a list of data-point objects whose keys are the dimension name, the metric name, and '{metric}_percentage'."
                    },
                    "generated_at": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Generated At"
                    },
                    "api_version": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Api Version"
                    },
                    "short_code": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Short Code"
                    },
                    "time_bucket_info": {
                        "anyOf": [
                            {
                                "$ref": "#/components/schemas/TimeBucketInfo"
                            },
                            {
                                "type": "null"
                            }
                        ]
                    },
                    "computed_metrics": {
                        "anyOf": [
                            {
                                "$ref": "#/components/schemas/ComputedMetrics"
                            },
                            {
                                "type": "null"
                            }
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "scope",
                    "filters",
                    "group_by",
                    "timezone",
                    "time_range",
                    "summary"
                ],
                "title": "StatsResponse",
                "description": "Response body for GET /api/v1/stats.\n\n``metrics`` uses dynamic keys ({metric}_by_{dimension}), each mapping to a\nlist of data-point dicts.  Optional fields (``short_code``,\n``time_bucket_info``, ``computed_metrics``) are absent when not applicable."
            },
            "StatsSummary": {
                "properties": {
                    "total_clicks": {
                        "type": "integer",
                        "title": "Total Clicks"
                    },
                    "unique_clicks": {
                        "type": "integer",
                        "title": "Unique Clicks"
                    },
                    "first_click": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "First Click"
                    },
                    "last_click": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Last Click"
                    },
                    "avg_redirection_time": {
                        "type": "number",
                        "title": "Avg Redirection Time"
                    }
                },
                "type": "object",
                "required": [
                    "total_clicks",
                    "unique_clicks",
                    "avg_redirection_time"
                ],
                "title": "StatsSummary",
                "description": "Summary statistics block inside StatsResponse."
            },
            "StatsTimeRange": {
                "properties": {
                    "start_date": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Start Date"
                    },
                    "end_date": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "End Date"
                    }
                },
                "type": "object",
                "title": "StatsTimeRange",
                "description": "Time range metadata inside StatsResponse."
            },
            "TimeBucketInfo": {
                "properties": {
                    "strategy": {
                        "type": "string",
                        "title": "Strategy"
                    },
                    "mongo_format": {
                        "type": "string",
                        "title": "Mongo Format"
                    },
                    "display_format": {
                        "type": "string",
                        "title": "Display Format"
                    },
                    "timezone": {
                        "type": "string",
                        "title": "Timezone"
                    },
                    "interval_minutes": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Interval Minutes"
                    }
                },
                "type": "object",
                "required": [
                    "strategy",
                    "mongo_format",
                    "display_format",
                    "timezone"
                ],
                "title": "TimeBucketInfo",
                "description": "Time bucketing metadata \u2014 only present when 'time' is in group_by."
            },
            "UpdateUrlRequest": {
                "properties": {
                    "long_url": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Long Url",
                        "description": "New destination URL. Must be a valid http:// or https:// URL.",
                        "examples": [
                            "https://example.com/updated/url"
                        ]
                    },
                    "alias": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Alias",
                        "description": "New custom short code. Pass `null` to keep existing. Must be unique and available.",
                        "examples": [
                            "newlink"
                        ]
                    },
                    "password": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Password",
                        "description": "New password. Pass `null` to remove password protection.",
                        "examples": [
                            "newPass@456"
                        ]
                    },
                    "block_bots": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Block Bots",
                        "description": "Block known bot user agents. Pass `null` to keep existing setting."
                    },
                    "max_clicks": {
                        "anyOf": [
                            {
                                "type": "integer",
                                "minimum": 0.0
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Max Clicks",
                        "description": "New click limit. Pass `0` or `null` to remove the limit.",
                        "examples": [
                            500
                        ]
                    },
                    "expire_after": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expire After",
                        "description": "Expiration time. ISO 8601 string (e.g. `2025-12-31T23:59:59Z`) or Unix epoch seconds (e.g. `1735689599`). Pass `null` to remove.",
                        "examples": [
                            "2025-12-31T23:59:59Z",
                            1735689599
                        ]
                    },
                    "private_stats": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Private Stats",
                        "description": "Make statistics private (only owner can view). Pass `null` to keep existing."
                    },
                    "status": {
                        "anyOf": [
                            {
                                "type": "string",
                                "enum": [
                                    "ACTIVE",
                                    "INACTIVE"
                                ]
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Status",
                        "description": "URL status. ACTIVE enables redirects, INACTIVE disables them.",
                        "examples": [
                            "ACTIVE"
                        ]
                    }
                },
                "type": "object",
                "title": "UpdateUrlRequest",
                "description": "Request body for partially updating an existing shortened URL.\n\nAll fields are optional; only provided fields are updated.\nPass ``max_clicks=0`` or ``max_clicks=null`` to remove the limit.\nPass ``password=null`` (or omit) to remove password protection."
            },
            "UpdateUrlResponse": {
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "Id",
                        "description": "MongoDB ObjectId of the URL.",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    },
                    "alias": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Alias",
                        "description": "Short code.",
                        "examples": [
                            "mylink"
                        ]
                    },
                    "long_url": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Long Url",
                        "description": "Destination URL.",
                        "examples": [
                            "https://example.com/long/url"
                        ]
                    },
                    "status": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Status",
                        "description": "URL status.",
                        "examples": [
                            "ACTIVE"
                        ]
                    },
                    "password_set": {
                        "type": "boolean",
                        "title": "Password Set",
                        "description": "Whether the URL is password-protected."
                    },
                    "max_clicks": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Max Clicks",
                        "description": "Click limit, or null if unlimited.",
                        "examples": [
                            100
                        ]
                    },
                    "expire_after": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expire After",
                        "description": "Expiration as Unix timestamp, or null.",
                        "examples": [
                            1735689599
                        ]
                    },
                    "block_bots": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Block Bots",
                        "description": "Whether bot blocking is enabled."
                    },
                    "private_stats": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Private Stats",
                        "description": "Whether statistics are private."
                    },
                    "updated_at": {
                        "type": "integer",
                        "title": "Updated At",
                        "description": "Last update time as Unix timestamp.",
                        "examples": [
                            1704067200
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "id",
                    "password_set",
                    "updated_at"
                ],
                "title": "UpdateUrlResponse",
                "description": "Response body after a successful URL update (PATCH /api/v1/urls/{url_id})."
            },
            "UpdateUrlStatusRequest": {
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": [
                            "ACTIVE",
                            "INACTIVE"
                        ],
                        "title": "Status",
                        "description": "New status for the URL. `ACTIVE` enables redirects, `INACTIVE` disables them.",
                        "examples": [
                            "ACTIVE"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "status"
                ],
                "title": "UpdateUrlStatusRequest",
                "description": "Request body for updating only the status of a shortened URL."
            },
            "UrlListItem": {
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "Id"
                    },
                    "alias": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Alias"
                    },
                    "long_url": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Long Url"
                    },
                    "status": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Status"
                    },
                    "created_at": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Created At"
                    },
                    "expire_after": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Expire After"
                    },
                    "max_clicks": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Max Clicks"
                    },
                    "private_stats": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Private Stats"
                    },
                    "block_bots": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Block Bots"
                    },
                    "password_set": {
                        "type": "boolean",
                        "title": "Password Set"
                    },
                    "total_clicks": {
                        "anyOf": [
                            {
                                "type": "integer"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Total Clicks"
                    },
                    "last_click": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Last Click"
                    }
                },
                "type": "object",
                "required": [
                    "id",
                    "password_set"
                ],
                "title": "UrlListItem",
                "description": "A single URL entry inside UrlListResponse.items.\n\n``created_at`` and ``last_click`` are ISO 8601 strings (e.g. \"2024-01-01T00:00:00Z\").\n``expire_after`` is a Unix timestamp integer or null.\nThese formats match the existing endpoint exactly."
            },
            "UrlListResponse": {
                "properties": {
                    "items": {
                        "items": {
                            "$ref": "#/components/schemas/UrlListItem"
                        },
                        "type": "array",
                        "title": "Items"
                    },
                    "page": {
                        "type": "integer",
                        "title": "Page"
                    },
                    "pageSize": {
                        "type": "integer",
                        "title": "Pagesize"
                    },
                    "total": {
                        "type": "integer",
                        "title": "Total"
                    },
                    "hasNext": {
                        "type": "boolean",
                        "title": "Hasnext"
                    },
                    "sortBy": {
                        "type": "string",
                        "title": "Sortby"
                    },
                    "sortOrder": {
                        "type": "string",
                        "title": "Sortorder"
                    }
                },
                "type": "object",
                "required": [
                    "items",
                    "page",
                    "pageSize",
                    "total",
                    "hasNext",
                    "sortBy",
                    "sortOrder"
                ],
                "title": "UrlListResponse",
                "description": "Response body for GET /api/v1/urls.\n\nUses camelCase field names to match the existing Flask endpoint exactly.\nField names are camelCase here (not snake_case + alias) because this is a\nresponse-only model \u2014 we build it explicitly in the route handler."
            },
            "UrlResponse": {
                "properties": {
                    "alias": {
                        "type": "string",
                        "title": "Alias",
                        "description": "Short code for the URL.",
                        "examples": [
                            "mylink"
                        ]
                    },
                    "short_url": {
                        "type": "string",
                        "title": "Short Url",
                        "description": "Full shortened URL ready for sharing.",
                        "examples": [
                            "https://spoo.me/mylink"
                        ]
                    },
                    "long_url": {
                        "type": "string",
                        "title": "Long Url",
                        "description": "Original destination URL.",
                        "examples": [
                            "https://example.com/long/url"
                        ]
                    },
                    "owner_id": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Owner Id",
                        "description": "User ID if authenticated, null for anonymous URLs.",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    },
                    "created_at": {
                        "type": "integer",
                        "title": "Created At",
                        "description": "Creation time as Unix timestamp.",
                        "examples": [
                            1704067200
                        ]
                    },
                    "status": {
                        "type": "string",
                        "title": "Status",
                        "description": "URL status.",
                        "examples": [
                            "ACTIVE"
                        ]
                    },
                    "private_stats": {
                        "anyOf": [
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Private Stats",
                        "description": "Whether statistics are private (owner-only)."
                    }
                },
                "type": "object",
                "required": [
                    "alias",
                    "short_url",
                    "long_url",
                    "created_at",
                    "status"
                ],
                "title": "UrlResponse",
                "description": "Response body for a newly created shortened URL (POST /api/v1/shorten).\n\n``created_at`` is a Unix timestamp integer \u2014 matching the existing endpoint."
            },
            "UserPfp": {
                "properties": {
                    "url": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Url",
                        "description": "Profile picture URL",
                        "examples": [
                            "https://lh3.googleusercontent.com/a/photo"
                        ]
                    },
                    "source": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Source",
                        "description": "Source of the profile picture",
                        "examples": [
                            "google"
                        ]
                    }
                },
                "type": "object",
                "title": "UserPfp",
                "description": "Profile picture info returned inside UserProfileResponse."
            },
            "UserProfileResponse": {
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "Id",
                        "description": "User ID",
                        "examples": [
                            "507f1f77bcf86cd799439011"
                        ]
                    },
                    "email": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Email",
                        "description": "User's email address",
                        "examples": [
                            "user@example.com"
                        ]
                    },
                    "email_verified": {
                        "type": "boolean",
                        "title": "Email Verified",
                        "description": "Whether the email address has been verified"
                    },
                    "user_name": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "User Name",
                        "description": "Display name",
                        "examples": [
                            "Jane Doe"
                        ]
                    },
                    "plan": {
                        "type": "string",
                        "title": "Plan",
                        "description": "Subscription plan",
                        "examples": [
                            "free"
                        ]
                    },
                    "password_set": {
                        "type": "boolean",
                        "title": "Password Set",
                        "description": "Whether the user has set a password"
                    },
                    "auth_providers": {
                        "items": {
                            "$ref": "#/components/schemas/AuthProviderInfo"
                        },
                        "type": "array",
                        "title": "Auth Providers",
                        "description": "Linked OAuth providers"
                    },
                    "pfp": {
                        "anyOf": [
                            {
                                "$ref": "#/components/schemas/UserPfp"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "description": "Profile picture (absent when not set)"
                    }
                },
                "type": "object",
                "required": [
                    "id",
                    "email_verified",
                    "plan",
                    "password_set",
                    "auth_providers"
                ],
                "title": "UserProfileResponse",
                "description": "User profile shape \u2014 used in login/register/me responses."
            },
            "ValidationError": {
                "properties": {
                    "loc": {
                        "items": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "integer"
                                }
                            ]
                        },
                        "type": "array",
                        "title": "Location"
                    },
                    "msg": {
                        "type": "string",
                        "title": "Message"
                    },
                    "type": {
                        "type": "string",
                        "title": "Error Type"
                    },
                    "input": {
                        "title": "Input"
                    },
                    "ctx": {
                        "type": "object",
                        "title": "Context"
                    }
                },
                "type": "object",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "title": "ValidationError"
            },
            "VerifyEmailRequest": {
                "properties": {
                    "code": {
                        "type": "string",
                        "title": "Code",
                        "description": "6-digit OTP from verification email",
                        "examples": [
                            "123456"
                        ]
                    }
                },
                "type": "object",
                "required": [
                    "code"
                ],
                "title": "VerifyEmailRequest",
                "description": "Request body for POST /auth/verify-email.\n\n``code`` is the 6-digit OTP sent to the user's email address."
            },
            "VerifyEmailResponse": {
                "properties": {
                    "success": {
                        "type": "boolean",
                        "title": "Success",
                        "description": "Whether verification succeeded"
                    },
                    "message": {
                        "type": "string",
                        "title": "Message",
                        "description": "Human-readable status message",
                        "examples": [
                            "email verified successfully"
                        ]
                    },
                    "email_verified": {
                        "type": "boolean",
                        "title": "Email Verified",
                        "description": "Updated email verification status (always true on success)"
                    }
                },
                "type": "object",
                "required": [
                    "success",
                    "message",
                    "email_verified"
                ],
                "title": "VerifyEmailResponse",
                "description": "Response body for POST /auth/verify-email (200)."
            }
        },
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "spoo_<key>",
                "description": "API key authentication. Pass your key as: `Bearer spoo_<your_key>`"
            },
            "JWTAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
                "description": "JWT access token from /auth/login. Pass as: `Bearer <jwt_token>`"
            }
        }
    },
    "tags": [
        {
            "name": "URL Shortening",
            "description": "Create new shortened URLs"
        },
        {
            "name": "Link Management",
            "description": "List, update, and delete your shortened URLs"
        },
        {
            "name": "Statistics",
            "description": "Click analytics and data export"
        },
        {
            "name": "API Keys",
            "description": "Create and manage API keys for programmatic access"
        },
        {
            "name": "Authentication",
            "description": "Login, register, password management, and email verification"
        },
        {
            "name": "OAuth",
            "description": "OAuth provider login, linking, and unlinking"
        },
        {
            "name": "System",
            "description": "Health checks and server metrics"
        }
    ],
    "servers": [
        {
            "url": "https://spoo.me",
            "description": "Production"
        }
    ],
    "security": [
        {
            "ApiKeyAuth": []
        },
        {
            "JWTAuth": []
        }
    ]
}