{
    "openapi": "3.1.0",
    "info": {
        "title": "Welleva API",
        "version": "1.0.0",
        "summary": "Backend for Welleva — a personal health operating system for iOS.",
        "description": "The Welleva backend serves the iOS app. The app is **local-first**: roughly 90% of features work\nentirely on device, and this API exists only for what genuinely needs a server — AI food analysis,\nthe nutrition catalogue, social features, billing, push and remote configuration.\n\n## Response shape\n\nEvery success is enveloped: `{\"data\": …}`, plus `{\"meta\": …}` for pagination, counts and quota\nstate. Every failure is `{\"error\": {\"code\", \"message\", \"details\", \"request_id\"}}`. Branch on\n`error.code`, which is stable, never on the message, which is localised and may change.\n\n## Authentication\n\n`Authorization: Bearer <access_token>`. Access tokens last 15 minutes; refresh tokens are opaque,\nsingle-use and rotated on every refresh. Presenting an already-rotated refresh token revokes the\nentire session family — treat `401 token_reuse_detected` as \"sign in again\".\n\nThe session is verified against the database on every request, so a sign-out takes effect\nimmediately rather than when the token expires.\n\n## The privacy boundary\n\nRaw HealthKit samples never leave the device. This API accepts **daily aggregates only**, and\nignores anything outside its column allow-list. There are no heart-rate series, no sleep sessions,\nno workout routes and no GPS coordinates on this server — Nearby stores a ~5 km geohash, and plate\nphotos are stripped of EXIF and deleted within 24 hours.\n\n## Errors\n\n| Status | Meaning |\n|---|---|\n| 400 | Malformed request (bad JSON) |\n| 401 | Missing, expired, revoked or reused token |\n| 402 | Free-tier limit or Premium-only capability — `details.paywall` names the paywall to show |\n| 403 | Authenticated but not permitted: consent missing, opt-in required, attestation failed |\n| 404 | No such resource — also returned when a resource exists but you must not learn that |\n| 405 | Wrong HTTP method (the `Allow` header lists the valid ones) |\n| 409 | Conflicts with current state, or an idempotency key was reused with a different body |\n| 422 | Validation failed — `details.fields` maps each field to its messages |\n| 429 | Rate limited — honour `Retry-After` |\n| 503 | A dependency is down, or a spend guardrail tripped — honour `Retry-After` |\n\n## Idempotency\n\nSend `X-Idempotency-Key` on anything that costs money or creates a resource. A replay returns the\nstored response instead of repeating the work, which is what stops a dropped response on a mobile\nnetwork from billing a second AI analysis.\n\n## Rate limits\n\nPer-endpoint buckets, keyed on the user when authenticated and the client IP otherwise. Limits are\nnoted on each operation. Cost-bearing endpoints fail closed if the limiter is unavailable.",
        "contact": {
            "name": "Welleva Engineering",
            "email": "suporte@welleva.app"
        },
        "license": {
            "name": "Proprietary",
            "identifier": "LicenseRef-Proprietary"
        }
    },
    "servers": [
        {
            "url": "https://welleva.app",
            "description": "Production"
        }
    ],
    "tags": [
        {
            "name": "Health",
            "description": "Liveness and readiness probes."
        },
        {
            "name": "Authentication",
            "description": "Sign in with Apple, session rotation, device sessions."
        },
        {
            "name": "Attestation",
            "description": "App Attest enrolment for the cost-bearing AI routes."
        },
        {
            "name": "Profile",
            "description": "Account, onboarding, goals, privacy settings and consent."
        },
        {
            "name": "AI",
            "description": "Food analysis from photographs and text."
        },
        {
            "name": "Catalogue",
            "description": "Nutrition database: search, barcode, contributions."
        },
        {
            "name": "Sync",
            "description": "Daily health aggregates. Raw HealthKit samples never leave the device."
        },
        {
            "name": "Social",
            "description": "Friends, blocks, reports and the achievement feed."
        },
        {
            "name": "Challenges",
            "description": "Group challenges with live leaderboards."
        },
        {
            "name": "Leagues",
            "description": "Weekly consistency leagues."
        },
        {
            "name": "Nearby",
            "description": "Opt-in discovery using approximate location only."
        },
        {
            "name": "Wrapped",
            "description": "Monthly and annual health recaps with population percentiles."
        },
        {
            "name": "Billing",
            "description": "Subscriptions, entitlements and App Store notifications."
        },
        {
            "name": "Config",
            "description": "Signed remote configuration and experiments."
        },
        {
            "name": "Telemetry",
            "description": "Product analytics ingestion."
        },
        {
            "name": "Push",
            "description": "APNs tokens and notification preferences."
        },
        {
            "name": "Privacy",
            "description": "LGPD/GDPR data export and account deletion."
        },
        {
            "name": "Marketing",
            "description": "Pre-launch waitlist for the landing page."
        },
        {
            "name": "Backoffice",
            "description": "Console de operação: identidade própria, sessão por cookie e 2FA obrigatório."
        },
        {
            "name": "Admin",
            "description": "Internal operations: margin, moderation, configuration."
        }
    ],
    "paths": {
        "/v1/admin/audit": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Audit log",
                "description": "Security- and privacy-relevant actions: config changes, suspensions, deletions, token reuse.",
                "parameters": [
                    {
                        "name": "action",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Audit entries"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminAudit",
                "security": []
            }
        },
        "/v1/admin/auth/login": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Etapa de senha",
                "description": "Primeira das duas etapas. Emite um cookie de sessão que **não libera nada** até o TOTP passar, então uma senha roubada sozinha não alcança rota alguma.\n\nConta inexistente e senha errada devolvem exatamente a mesma resposta, para que o endpoint não sirva de lista de operadores.\n\n**Rate limit:** 10 requests / 900s (bucket `admin_login`).",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "email",
                                    "password"
                                ],
                                "properties": {
                                    "email": {
                                        "type": "string",
                                        "format": "email"
                                    },
                                    "password": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Senha aceita; falta o segundo fator"
                    },
                    "401": {
                        "description": "Credenciais inválidas"
                    },
                    "403": {
                        "description": "Conta de operador suspensa"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAdminAuthLogin",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/logout": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Encerrar a sessão",
                "security": [],
                "responses": {
                    "204": {
                        "description": "Sessão encerrada"
                    }
                },
                "operationId": "postAdminAuthLogout",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/me": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Operador atual, permissões e token CSRF",
                "description": "O painel chama isto ao carregar: estabelece se há sessão viva e entrega o token CSRF das escritas.",
                "responses": {
                    "200": {
                        "description": "Contexto do operador"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getAdminAuthMe",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/mfa": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Etapa do segundo fator",
                "description": "Aceita um código TOTP ou um código de recuperação de uso único. Só aqui a sessão passa a valer.\n\n**Rate limit:** 12 requests / 900s (bucket `admin_mfa`).",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "code"
                                ],
                                "properties": {
                                    "code": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Autenticado"
                    },
                    "401": {
                        "description": "Código inválido ou sessão expirada"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAdminAuthMfa",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/mfa/confirm": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Confirmar o 2FA e receber os códigos de recuperação",
                "description": "Os códigos são exibidos uma única vez e cada um vale um uso.\n\n**Rate limit:** 12 requests / 900s (bucket `admin_mfa`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "code"
                                ],
                                "properties": {
                                    "code": {
                                        "type": "string",
                                        "minLength": 6,
                                        "maxLength": 6
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "2FA ativo"
                    },
                    "401": {
                        "description": "Código inválido"
                    }
                },
                "operationId": "postAdminAuthMfaConfirm",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/mfa/setup": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Iniciar cadastro do 2FA",
                "description": "Gera um segredo e a URI `otpauth://`. O segredo fica inutilizável até ser confirmado.\n\n**Rate limit:** 12 requests / 900s (bucket `admin_mfa`).",
                "responses": {
                    "200": {
                        "description": "Segredo e URI"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "postAdminAuthMfaSetup",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/password": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Trocar a senha",
                "description": "Encerra todas as outras sessões — que é justamente o motivo de trocar a senha depois de uma suspeita.\n\n**Rate limit:** 10 requests / 900s (bucket `admin_login`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "current_password",
                                    "new_password"
                                ],
                                "properties": {
                                    "current_password": {
                                        "type": "string"
                                    },
                                    "new_password": {
                                        "type": "string",
                                        "minLength": 12
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Senha alterada"
                    },
                    "401": {
                        "description": "Senha atual incorreta"
                    }
                },
                "operationId": "postAdminAuthPassword",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/sessions": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Sessões ativas do operador",
                "responses": {
                    "200": {
                        "description": "Sessões"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getAdminAuthSessions",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/auth/sessions/revoke-others": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Encerrar as outras sessões",
                "responses": {
                    "200": {
                        "description": "Sessões encerradas"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "postAdminAuthSessionsRevokeothers",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/catalog/pending": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "User-contributed foods awaiting review",
                "responses": {
                    "200": {
                        "description": "Pending items"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminCatalogPending",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/catalog/{id}": {
            "patch": {
                "tags": [
                    "Admin"
                ],
                "summary": "Approve or reject a contributed food",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "status"
                                ],
                                "properties": {
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "active",
                                            "rejected",
                                            "merged"
                                        ]
                                    },
                                    "verified": {
                                        "type": "boolean"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Moderated"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "patchAdminCatalogById",
                "security": []
            }
        },
        "/v1/admin/config": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "List remote-config entries",
                "responses": {
                    "200": {
                        "description": "Entries"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminConfig",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/config/{key}": {
            "put": {
                "tags": [
                    "Admin"
                ],
                "summary": "Set a config value",
                "description": "Supports percentage rollout and audience segmentation. Every change is written to the audit log.",
                "parameters": [
                    {
                        "name": "key",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "value"
                                ],
                                "properties": {
                                    "value": {
                                        "description": "Any JSON value."
                                    },
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "bool",
                                            "int",
                                            "float",
                                            "string",
                                            "json"
                                        ]
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "enabled": {
                                        "type": "boolean"
                                    },
                                    "rollout_percent": {
                                        "type": "integer",
                                        "minimum": 0,
                                        "maximum": 100
                                    },
                                    "is_public": {
                                        "type": "boolean",
                                        "description": "False keeps the value server-side; it is never sent to the app."
                                    },
                                    "min_app_version": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "putAdminConfigByKey",
                "security": []
            },
            "delete": {
                "tags": [
                    "Admin"
                ],
                "summary": "Delete a config entry",
                "parameters": [
                    {
                        "name": "key",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Deleted"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "deleteAdminConfigByKey",
                "security": []
            }
        },
        "/v1/admin/cost/top-spenders": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Highest AI spend per user",
                "description": "The fair-use watch list: who is at the top of the cost distribution on a given day.",
                "parameters": [
                    {
                        "name": "day",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Top spenders"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminCostTopspenders",
                "security": []
            }
        },
        "/v1/admin/funnel": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Onboarding and conversion funnel",
                "description": "Unique users at each step from `app_opened` through `purchase_completed`, with step-to-step conversion.",
                "responses": {
                    "200": {
                        "description": "Funnel"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminFunnel",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/growth/ai-cost": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Custo de IA por dia, recurso e modelo",
                "responses": {
                    "200": {
                        "description": "Custo"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminGrowthAicost",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/growth/retention": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Retenção D1/D7/D30 por coorte semanal",
                "responses": {
                    "200": {
                        "description": "Coortes"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminGrowthRetention",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/growth/revenue": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Receita, MRR e conversão de trial",
                "description": "O MRR conta apenas assinaturas pagantes. Incluir trials inflaria o número sem nenhuma receita por trás, e a conversão só considera trials que já tiveram tempo de se resolver.",
                "responses": {
                    "200": {
                        "description": "Receita"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminGrowthRevenue",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/growth/waitlist": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Lista de espera, com origem e objetivo",
                "responses": {
                    "200": {
                        "description": "Cadastros"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminGrowthWaitlist",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/growth/waitlist/export": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Exportar a lista de espera em CSV",
                "description": "A exportação é auditada: é uma leitura em massa de dados pessoais e deve deixar rastro.",
                "responses": {
                    "200": {
                        "description": "CSV"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminGrowthWaitlistExport",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/margin": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Contribution margin report",
                "description": "Net revenue after Apple's commission and tax, minus AI spend — the measurement behind the ≥40% floor.\n\n`margin.below_floor` is what the daily watchdog alerts on.",
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Margin report"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminMargin",
                "security": []
            }
        },
        "/v1/admin/metrics": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Platform metrics",
                "description": "DAU/WAU/MAU, conversion, AI cost and API health. Requires an admin account.",
                "responses": {
                    "200": {
                        "description": "Metrics"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminMetrics",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/notifications/stats": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Notification delivery and open rates by rule and variant",
                "responses": {
                    "200": {
                        "description": "Stats"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminNotificationsStats",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/admin/reports": {
            "get": {
                "tags": [
                    "Admin"
                ],
                "summary": "Moderation queue",
                "description": "Open user reports, ordered by how many times the reported account has been reported.",
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "open",
                                "reviewing",
                                "actioned",
                                "dismissed"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Reports"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminReports",
                "security": []
            }
        },
        "/v1/admin/reports/{id}": {
            "patch": {
                "tags": [
                    "Admin"
                ],
                "summary": "Resolve a report",
                "description": "`action: suspend_user` suspends the reported account and revokes all its sessions immediately.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "status"
                                ],
                                "properties": {
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "reviewing",
                                            "actioned",
                                            "dismissed"
                                        ]
                                    },
                                    "note": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "action": {
                                        "type": "string",
                                        "enum": [
                                            "none",
                                            "suspend_user"
                                        ]
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Resolved"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "patchAdminReportsById",
                "security": []
            }
        },
        "/v1/admin/users": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Listar e buscar contas",
                "description": "Busca por username, nome, e-mail ou id público — o que o operador tiver em mãos no atendimento.",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "plan",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "free",
                                "premium",
                                "lifetime"
                            ]
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "active",
                                "suspended",
                                "pending_deletion"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Contas"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "getAdminUsers",
                "security": []
            }
        },
        "/v1/admin/users/{id}": {
            "get": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Detalhe de uma conta",
                "description": "Expõe o resumo agregado que um atendimento precisa — plano, cobertura, sequências, assinatura — e nunca o histórico de saúde dia a dia.\n\n**Cada leitura é registrada na auditoria**: abrir o histórico de alguém é, em si, um ato auditável.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Conta"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getAdminUsersById",
                "security": []
            }
        },
        "/v1/admin/users/{id}/entitlement": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Conceder ou remover plano manualmente",
                "description": "Gravado com `source = admin`, para nunca ser confundido com uma compra real da App Store nos números de receita.\n\n**Rate limit:** 120 requests / 60s (bucket `admin_write`).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "plan",
                                    "reason"
                                ],
                                "properties": {
                                    "plan": {
                                        "type": "string",
                                        "enum": [
                                            "free",
                                            "premium",
                                            "lifetime"
                                        ]
                                    },
                                    "days": {
                                        "type": "integer",
                                        "nullable": true
                                    },
                                    "reason": {
                                        "type": "string",
                                        "minLength": 3
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Plano atualizado"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "postAdminUsersByIdEntitlement",
                "security": []
            }
        },
        "/v1/admin/users/{id}/notes": {
            "post": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Anotação interna sobre a conta",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "note"
                                ],
                                "properties": {
                                    "note": {
                                        "type": "string",
                                        "maxLength": 2000
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Anotação salva"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "postAdminUsersByIdNotes",
                "security": []
            }
        },
        "/v1/admin/users/{id}/notes/{noteId}": {
            "delete": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Remover uma anotação",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "noteId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Removida"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "deleteAdminUsersByIdNotesByNoteId",
                "security": []
            }
        },
        "/v1/admin/users/{id}/status": {
            "patch": {
                "tags": [
                    "Backoffice"
                ],
                "summary": "Suspender ou reativar uma conta",
                "description": "Suspender encerra as sessões imediatamente, não quando os tokens expirarem.\n\n**Rate limit:** 120 requests / 60s (bucket `admin_write`).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "status"
                                ],
                                "properties": {
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "active",
                                            "suspended"
                                        ]
                                    },
                                    "reason": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Status alterado"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    }
                },
                "operationId": "patchAdminUsersByIdStatus",
                "security": []
            }
        },
        "/v1/ai/analyses": {
            "get": {
                "tags": [
                    "AI"
                ],
                "summary": "Recent analyses",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "History"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getAiAnalyses",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/ai/analyses/{id}": {
            "patch": {
                "tags": [
                    "AI"
                ],
                "summary": "Correct an analysis",
                "description": "Records the user's corrected item list.\n\nThis is the highest-signal data the product generates: it is precisely where the model was wrong, labelled by someone looking at the actual plate. Send it whenever the user edits a result — it drives prompt and reference-portion tuning.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "items"
                                ],
                                "properties": {
                                    "items": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/AnalysisItem"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Correction recorded"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "patchAiAnalysesById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/ai/food/photo": {
            "post": {
                "tags": [
                    "AI"
                ],
                "summary": "Analyse a meal photograph",
                "description": "Sends a plate photo for nutritional analysis and returns identified items with estimated portions and\nmacros. Tuned for Brazilian food: the prompt carries reference portions for arroz, feijão, farofa,\npão francês, marmita and the rest, because a generic vision model sizes these against American\nreference portions and gets them badly wrong.\n\n**Before the call reaches a model:**\n1. `ai_photo_processing` consent must be granted.\n2. An App Attest assertion must be present (when attestation is enabled).\n3. The plan quota is consumed — falling back to purchased credits when the allowance is gone.\n4. Per-user and global daily spend caps are checked.\n\n**Cost behaviour.** Identical photos are content-addressed by SHA-256, so a re-send is a free cache\nhit (`cached: true`). The fast model runs first; a low-confidence result escalates to the accurate\nmodel only while the day's escalation rate is under its cap.\n\n**The image never keeps its metadata.** It is re-encoded server-side, which strips EXIF — a plate\nphoto will not ship the user's GPS coordinates to the model provider. The file is deleted within 24\nhours.\n\nSend `X-Idempotency-Key`: mobile networks drop responses, and without it a retry pays for the\nanalysis twice.\n\n**When the model cannot see food**, it returns `items: []` with an explanatory `note` rather than\ninventing a meal. Render the note; do not treat it as an error.\n\n**Rate limit:** 10 requests / 60s (bucket `ai_photo`).\n\n**Device attestation required** when App Attest is enabled: send `X-Attest-Key-Id`, `X-Attest-Assertion` and `X-Attest-Client-Data`.\n\n**Consent required:** `ai_photo_processing` must be granted via `POST /v1/me/consents`.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "photo"
                                ],
                                "properties": {
                                    "photo": {
                                        "type": "string",
                                        "format": "binary",
                                        "description": "JPEG, PNG, HEIC or WebP. Max 6 MB. Downscaled to 1568px server-side."
                                    },
                                    "meal_type": {
                                        "type": "string",
                                        "enum": [
                                            "breakfast",
                                            "lunch",
                                            "dinner",
                                            "snack"
                                        ]
                                    },
                                    "hint": {
                                        "type": "string",
                                        "maxLength": 200,
                                        "description": "Optional user note, e.g. \"sem arroz\"."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Analysis complete",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/FoodAnalysis"
                                        },
                                        "meta": {
                                            "type": "object"
                                        }
                                    }
                                },
                                "example": {
                                    "data": {
                                        "analysis_id": "anl_m8k2p4nq7xw3z9a",
                                        "items": [
                                            {
                                                "name": "Arroz branco cozido",
                                                "grams": 150,
                                                "kcal": 193,
                                                "protein_g": 3.6,
                                                "carbs_g": 42.3,
                                                "fat_g": 0.4,
                                                "confidence": 0.88,
                                                "portion_description": "1 escumadeira grande"
                                            },
                                            {
                                                "name": "Feijão carioca",
                                                "grams": 100,
                                                "kcal": 76,
                                                "protein_g": 4.8,
                                                "carbs_g": 13.6,
                                                "fat_g": 0.5,
                                                "confidence": 0.84,
                                                "portion_description": "1 concha"
                                            },
                                            {
                                                "name": "Bife grelhado",
                                                "grams": 110,
                                                "kcal": 231,
                                                "protein_g": 31.2,
                                                "carbs_g": 0,
                                                "fat_g": 11.4,
                                                "confidence": 0.79,
                                                "portion_description": "1 unidade média"
                                            }
                                        ],
                                        "totals": {
                                            "kcal": 500,
                                            "protein_g": 39.6,
                                            "carbs_g": 55.9,
                                            "fat_g": 12.3,
                                            "grams": 360
                                        },
                                        "confidence": 0.84,
                                        "meal_type": "lunch",
                                        "cached": false,
                                        "model": "claude-haiku-4-5"
                                    },
                                    "meta": {
                                        "quota": {
                                            "used": 1,
                                            "limit": 3,
                                            "remaining": 2,
                                            "period": "week",
                                            "source": "plan"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "description": "Consent missing, or device attestation failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "$ref": "#/components/responses/Conflict"
                    },
                    "503": {
                        "$ref": "#/components/responses/ServiceUnavailable"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAiFoodPhoto",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/ai/food/text": {
            "post": {
                "tags": [
                    "AI"
                ],
                "summary": "Analyse a meal description",
                "description": "Parses natural language — \"dois ovos mexidos e um pão francês\" — into items with macros.\n\nCheaper and more accurate than photo analysis when the user states quantities, because there is no portion estimation to get wrong. Matching catalogue entries are injected into the prompt so the model anchors on real TACO values rather than its own recollection of them.\n\n**Rate limit:** 20 requests / 60s (bucket `ai_text`).\n\n**Device attestation required** when App Attest is enabled: send `X-Attest-Key-Id`, `X-Attest-Assertion` and `X-Attest-Client-Data`.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "description"
                                ],
                                "properties": {
                                    "description": {
                                        "type": "string",
                                        "minLength": 3,
                                        "maxLength": 500
                                    },
                                    "meal_type": {
                                        "type": "string",
                                        "enum": [
                                            "breakfast",
                                            "lunch",
                                            "dinner",
                                            "snack"
                                        ]
                                    }
                                }
                            },
                            "example": {
                                "description": "2 ovos mexidos, um pão francês com manteiga e um café preto",
                                "meal_type": "breakfast"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Analysis complete",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/FoodAnalysis"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "503": {
                        "$ref": "#/components/responses/ServiceUnavailable"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAiFoodText",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/attest/challenge": {
            "post": {
                "tags": [
                    "Attestation"
                ],
                "summary": "Request an App Attest challenge",
                "description": "Step 1 of App Attest enrolment. Returns a single-use, 5-minute challenge to pass to\n`DCAppAttestService.attestKey(_:clientDataHash:)`.\n\nWhen attestation is not enabled on the server, this returns `{\"enabled\": false}` and the app should\nskip enrolment entirely rather than treating it as an error.\n\n**Rate limit:** 20 requests / 3600s (bucket `attest_challenge`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "device_uuid"
                                ],
                                "properties": {
                                    "device_uuid": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Challenge issued",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "enabled": {
                                                    "type": "boolean"
                                                },
                                                "challenge": {
                                                    "type": "string",
                                                    "nullable": true
                                                },
                                                "expires_in": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAttestChallenge",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/attest/key": {
            "post": {
                "tags": [
                    "Attestation"
                ],
                "summary": "Register an attested key",
                "description": "Step 2. Submits the attestation object from the Secure Enclave.\n\nThe server verifies the full Apple certificate chain, that the embedded nonce matches the challenge\nit issued, that the key id is the SHA-256 of the certificate's public key, and that the attestation is\nbound to this app's Team ID and bundle id. Any mismatch is refused.\n\nOnce registered, every `/v1/ai/*` call must carry an assertion signed by this key.\n\n**Rate limit:** 20 requests / 3600s (bucket `attest_challenge`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "device_uuid",
                                    "key_id",
                                    "attestation_object",
                                    "challenge"
                                ],
                                "properties": {
                                    "device_uuid": {
                                        "type": "string"
                                    },
                                    "key_id": {
                                        "type": "string",
                                        "description": "Base64 key identifier from `generateKey`."
                                    },
                                    "attestation_object": {
                                        "type": "string",
                                        "description": "Base64 CBOR attestation object."
                                    },
                                    "challenge": {
                                        "type": "string",
                                        "description": "The challenge from step 1."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Key registered"
                    },
                    "403": {
                        "description": "Attestation failed verification",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAttestKey",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/apple": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Sign in with Apple",
                "description": "Exchanges an Apple identity token for a Welleva session. Creates the account on first use.\n\nThe identity token is verified against Apple's live JWKS: RS256 signature, `iss`, `aud` (your bundle\nid), expiry, and — when you send one — the `nonce`. Sending a nonce is strongly recommended: it is\nwhat stops a token captured from another app being replayed here.\n\nApple returns the user's email **only on the very first authorisation**. Persist it then; it will not\nbe sent again.\n\nReturns **201** when the account was created, **200** on a subsequent sign-in.\n\n**Rate limit:** 10 requests / 300s (bucket `auth_apple`).",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "identity_token"
                                ],
                                "properties": {
                                    "identity_token": {
                                        "type": "string",
                                        "description": "The JWS from `ASAuthorizationAppleIDCredential.identityToken`."
                                    },
                                    "nonce": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "The raw nonce whose SHA-256 you passed to Apple."
                                    },
                                    "full_name": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Also first-authorisation only."
                                    },
                                    "device": {
                                        "type": "object",
                                        "properties": {
                                            "device_uuid": {
                                                "type": "string",
                                                "description": "Stable per install (`identifierForVendor`)."
                                            },
                                            "platform": {
                                                "type": "string",
                                                "enum": [
                                                    "ios",
                                                    "watchos",
                                                    "ipados"
                                                ]
                                            },
                                            "model": {
                                                "type": "string"
                                            },
                                            "os_version": {
                                                "type": "string"
                                            },
                                            "app_version": {
                                                "type": "string"
                                            },
                                            "timezone": {
                                                "type": "string",
                                                "example": "America/Sao_Paulo"
                                            },
                                            "locale": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "identity_token": "eyJraWQiOiJXNldjT0tCIiwiYWxnIjoiUlMyNTYifQ…",
                                "nonce": "a1b2c3d4e5f6",
                                "full_name": "Ana Silva",
                                "device": {
                                    "device_uuid": "B8F3…",
                                    "platform": "ios",
                                    "model": "iPhone16,2",
                                    "os_version": "18.2",
                                    "app_version": "1.0.0"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Signed in",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "user": {
                                                    "$ref": "#/components/schemas/User"
                                                },
                                                "entitlement": {
                                                    "$ref": "#/components/schemas/Entitlement"
                                                },
                                                "is_new_user": {
                                                    "type": "boolean"
                                                },
                                                "needs_username": {
                                                    "type": "boolean"
                                                },
                                                "tokens": {
                                                    "$ref": "#/components/schemas/Tokens"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "201": {
                        "description": "Account created"
                    },
                    "401": {
                        "description": "The Apple token failed verification",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "503": {
                        "$ref": "#/components/responses/ServiceUnavailable"
                    }
                },
                "operationId": "postAuthApple",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/logout": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Sign out this device",
                "description": "Revokes the current session. Takes effect immediately, not when the access token expires.",
                "responses": {
                    "204": {
                        "description": "Signed out"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "postAuthLogout",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/logout-all": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Sign out everywhere else",
                "description": "Revokes every other session, keeping the current one. Use after a suspected compromise.",
                "responses": {
                    "200": {
                        "description": "Sessions revoked",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "revoked_sessions": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "postAuthLogoutall",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/refresh": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Rotate the session",
                "description": "Exchanges a refresh token for a new pair. **The refresh token is single-use** — store the new one\nimmediately and discard the old.\n\nPresenting a token that was already rotated revokes the entire session family and returns\n`401 token_reuse_detected`. That is deliberate: either an attacker is replaying a stolen token or the\nclient is retrying, and since the two are indistinguishable, the safe response is to end the session.\nHandle this code by sending the user back to sign-in.\n\n**Rate limit:** 30 requests / 300s (bucket `auth_refresh`).",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "refresh_token"
                                ],
                                "properties": {
                                    "refresh_token": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "New token pair",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "tokens": {
                                                    "$ref": "#/components/schemas/Tokens"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unknown, expired, revoked or reused token",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postAuthRefresh",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/sessions": {
            "get": {
                "tags": [
                    "Authentication"
                ],
                "summary": "List active sessions",
                "description": "Powers a \"where you are signed in\" screen.",
                "responses": {
                    "200": {
                        "description": "Active sessions"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getAuthSessions",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/sessions/{id}": {
            "delete": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Revoke one session",
                "description": "Always returns 204, whether or not the session existed — a caller must not be able to probe for other users' session ids.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Revoked (or never existed)"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "deleteAuthSessionsById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/auth/username": {
            "put": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Claim a username",
                "description": "3–30 characters: lowercase letters, digits, dot or underscore. Reserved names are refused.\n\nRate limited to 2 changes per week — usernames are how friends find each other, so churn is disruptive.\n\n**Rate limit:** 2 requests / 604800s (bucket `username_change`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "username"
                                ],
                                "properties": {
                                    "username": {
                                        "type": "string",
                                        "pattern": "^[a-z0-9](?:[a-z0-9_.]{1,28}[a-z0-9])$",
                                        "example": "ana.silva"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Username claimed"
                    },
                    "409": {
                        "description": "Taken, reserved or invalid",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "putAuthUsername",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/auth/username-available": {
            "get": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Check username availability",
                "parameters": [
                    {
                        "name": "username",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Availability",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "username": {
                                                    "type": "string"
                                                },
                                                "valid": {
                                                    "type": "boolean"
                                                },
                                                "available": {
                                                    "type": "boolean"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getAuthUsernameavailable",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/billing/credits": {
            "get": {
                "tags": [
                    "Billing"
                ],
                "summary": "AI credit balance and ledger",
                "description": "Consumable credits from packs. Spent automatically once the plan allowance runs out.",
                "responses": {
                    "200": {
                        "description": "Balance and recent ledger"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getBillingCredits",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/billing/link": {
            "post": {
                "tags": [
                    "Billing"
                ],
                "summary": "Link a StoreKit purchase to this account",
                "description": "Call right after a successful StoreKit purchase so entitlement appears without waiting for Apple's\nnotification.\n\nThis is a **fast path, not a trust boundary**. If the signed notification has not arrived yet, the\nresponse is `202` with `linked: false` and no entitlement is granted — granting on an unverified\nclient claim would make the whole signature check pointless.\n\nSet `appAccountToken` to the user's `public_id` when starting the StoreKit purchase; that is what\nlets Apple's notification be attributed to an account at all.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "original_transaction_id",
                                    "product_id"
                                ],
                                "properties": {
                                    "original_transaction_id": {
                                        "type": "string"
                                    },
                                    "product_id": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Linked; entitlement active"
                    },
                    "202": {
                        "description": "Acknowledged; awaiting Apple's notification"
                    },
                    "409": {
                        "description": "That purchase belongs to another account",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postBillingLink",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/billing/products": {
            "get": {
                "tags": [
                    "Billing"
                ],
                "summary": "Product catalogue and plan comparison",
                "description": "Product identifiers plus the Free/Premium capability matrix, for rendering a paywall.\n\nPrices are **reference values only** — StoreKit price tiers are authoritative for the user's storefront.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Catalogue"
                    }
                },
                "operationId": "getBillingProducts",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/billing/subscription": {
            "get": {
                "tags": [
                    "Billing"
                ],
                "summary": "Current subscription and entitlement",
                "description": "The server's view of what the user is entitled to, plus their AI credit balance.",
                "responses": {
                    "200": {
                        "description": "Subscription state"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getBillingSubscription",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/billing/webhook": {
            "post": {
                "tags": [
                    "Billing"
                ],
                "summary": "App Store Server Notifications v2",
                "description": "Apple's webhook. **Not called by the app.**\n\nUnauthenticated by necessity — Apple cannot present a bearer token. The JWS signature *is* the\nauthentication: the payload's certificate chain is verified against Apple's Root CA G3, every\ncertificate's validity window is checked, and only then is the ES256 signature verified. An\nunverifiable payload is rejected outright, never processed \"just in case\".\n\nProcessing is **idempotent** (`notificationUUID` is unique, so Apple's retries cannot double-grant)\nand the raw payload is stored **before** processing, so a bug in the handler is replayable rather\nthan a permanently lost billing event.\n\nAlways answers 200 once the signature checks out. A non-2xx makes Apple retry, and retrying a payload\nthe handler cannot process just repeats the failure — the stored row is the recovery path.",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "signedPayload"
                                ],
                                "properties": {
                                    "signedPayload": {
                                        "type": "string",
                                        "description": "The JWS Apple sends."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Handled (processed, duplicate, ignored or recorded-as-failed)"
                    },
                    "400": {
                        "description": "Missing signedPayload",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Signature or certificate chain verification failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "operationId": "postBillingWebhook",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/catalog/barcode/{ean}": {
            "get": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Look up a barcode",
                "description": "Resolves EAN-8/13 or UPC-A. Order: local catalogue → cached lookup → Open Food Facts.\n\nProducts OFF does not know are cached as `not_found` for a week, so repeatedly scanning an unknown item does not generate an outbound request every time. A 404 includes `can_contribute: true` — prompt the user to add it via `POST /v1/catalog/items`.\n\n**Rate limit:** 90 requests / 60s (bucket `catalog_barcode`).",
                "parameters": [
                    {
                        "name": "ean",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^\\d{8,14}$"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Product found",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/FoodItem"
                                        },
                                        "meta": {
                                            "type": "object",
                                            "properties": {
                                                "source": {
                                                    "type": "string",
                                                    "enum": [
                                                        "catalog",
                                                        "cache",
                                                        "openfoodfacts"
                                                    ]
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found in any source",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "getCatalogBarcodeByEan",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/catalog/favorites": {
            "get": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Pinned foods",
                "responses": {
                    "200": {
                        "description": "Favourites"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getCatalogFavorites",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/catalog/favorites/{id}": {
            "put": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Pin a food",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Pinned"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "putCatalogFavoritesById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            },
            "delete": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Unpin a food",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Unpinned"
                    }
                },
                "operationId": "deleteCatalogFavoritesById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/catalog/items": {
            "post": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Contribute a food item",
                "description": "Adds a missing product. Enters as `pending_review`: immediately usable by its author, but it does not reach everyone's search results until moderated.\n\n**Macros are checked for physical coherence** — protein and carbs at 4 kcal/g, fat at 9 — with a 20% tolerance. An inconsistent entry returns `422 macros_inconsistent` with the derived figure, so the user can see what is wrong.\n\n**Rate limit:** 60 requests / 300s (bucket `social_write`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "name",
                                    "kcal_100",
                                    "protein_100",
                                    "carbs_100",
                                    "fat_100"
                                ],
                                "properties": {
                                    "name": {
                                        "type": "string"
                                    },
                                    "brand": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "barcode": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "category": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "is_liquid": {
                                        "type": "boolean"
                                    },
                                    "kcal_100": {
                                        "type": "number"
                                    },
                                    "protein_100": {
                                        "type": "number"
                                    },
                                    "carbs_100": {
                                        "type": "number"
                                    },
                                    "fat_100": {
                                        "type": "number"
                                    },
                                    "fiber_100": {
                                        "type": "number",
                                        "nullable": true
                                    },
                                    "sodium_mg_100": {
                                        "type": "number",
                                        "nullable": true
                                    },
                                    "default_serving_g": {
                                        "type": "number",
                                        "nullable": true
                                    },
                                    "portions": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "label": {
                                                    "type": "string"
                                                },
                                                "grams": {
                                                    "type": "number"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Submitted for review"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postCatalogItems",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/catalog/items/{id}": {
            "get": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Get one food item",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Item with portions and micronutrients"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getCatalogItemsById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/catalog/recent": {
            "get": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Recently used foods",
                "description": "Server-side, so a new device is immediately useful.",
                "responses": {
                    "200": {
                        "description": "Recent items"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getCatalogRecent",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/catalog/search": {
            "get": {
                "tags": [
                    "Catalogue"
                ],
                "summary": "Search foods",
                "description": "Full-text search over TACO (Brazil), USDA and Open Food Facts entries.\n\nMatching blends three signals, because none alone handles Brazilian food vocabulary: unaccented prefix\nmatching (\"pao frances\" finds \"Pão Francês\"), a synonym table for regional names (\"cinquentinha\"), and\nFULLTEXT relevance. Results are then ranked by how often people actually log them.\n\nResponses are cached for 15 minutes, which absorbs the typing burst of an autocomplete field.\n\n**Rate limit:** 120 requests / 60s (bucket `catalog_search`).",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "minLength": 2,
                            "maxLength": 80
                        }
                    },
                    {
                        "name": "category",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Matches",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/FoodItem"
                                            }
                                        },
                                        "meta": {
                                            "type": "object",
                                            "properties": {
                                                "query": {
                                                    "type": "string"
                                                },
                                                "count": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "getCatalogSearch",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges": {
            "get": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Your challenges",
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "upcoming",
                                "active",
                                "completed"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Challenges",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Challenge"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getChallenges",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            },
            "post": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Create a challenge",
                "description": "**Premium only.** Free users can join and compete in challenges friends create — which is what keeps the invite loop working without paywalling participation.\n\nMaximum duration 90 days, maximum 200 participants.\n\n**Rate limit:** 10 requests / 3600s (bucket `challenge_create`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "title",
                                    "metric",
                                    "starts_on",
                                    "ends_on"
                                ],
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "minLength": 3,
                                        "maxLength": 80
                                    },
                                    "description": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "metric": {
                                        "type": "string",
                                        "enum": [
                                            "steps",
                                            "water_ml",
                                            "active_minutes",
                                            "fasting_minutes",
                                            "sleep_minutes",
                                            "protein_g",
                                            "mindful_minutes",
                                            "daily_goal_hits",
                                            "workouts"
                                        ]
                                    },
                                    "mode": {
                                        "type": "string",
                                        "enum": [
                                            "total",
                                            "average",
                                            "streak",
                                            "goal_hits"
                                        ],
                                        "default": "total"
                                    },
                                    "target_value": {
                                        "type": "number",
                                        "nullable": true,
                                        "description": "Required for `goal_hits` and `streak` modes."
                                    },
                                    "visibility": {
                                        "type": "string",
                                        "enum": [
                                            "private",
                                            "friends",
                                            "public"
                                        ],
                                        "default": "friends"
                                    },
                                    "max_participants": {
                                        "type": "integer",
                                        "minimum": 2,
                                        "maximum": 200,
                                        "default": 50
                                    },
                                    "starts_on": {
                                        "type": "string",
                                        "format": "date"
                                    },
                                    "ends_on": {
                                        "type": "string",
                                        "format": "date"
                                    }
                                }
                            },
                            "example": {
                                "title": "Semana dos 10k passos",
                                "metric": "steps",
                                "mode": "total",
                                "visibility": "friends",
                                "starts_on": "2026-09-01",
                                "ends_on": "2026-09-07"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Challenge"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "409": {
                        "$ref": "#/components/responses/Conflict"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postChallenges",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/challenges/{id}": {
            "get": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Challenge detail with leaderboard",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Challenge",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Challenge"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getChallengesById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges/{id}/invite": {
            "post": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Invite people",
                "description": "Owner only. Returns which usernames were invited and which were skipped, with a reason.\n\n**Rate limit:** 60 requests / 300s (bucket `social_write`).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "usernames"
                                ],
                                "properties": {
                                    "usernames": {
                                        "type": "array",
                                        "maxItems": 50,
                                        "items": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Invitations processed"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postChallengesByIdInvite",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges/{id}/join": {
            "post": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Join a challenge",
                "description": "Private challenges need a `join_code` unless you were invited.\n\nJoining mid-challenge **backfills** the days already elapsed from your synced aggregates, so a late joiner does not start permanently behind.\n\nFree accounts may hold one active challenge at a time.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "join_code": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Joined",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Challenge"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "description": "Already joined, full, or closed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postChallengesByIdJoin",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges/{id}/leaderboard": {
            "get": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Live leaderboard",
                "description": "Ranked by progress. Participants set to `anonymous` appear without an identity; `hidden` are omitted entirely.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Leaderboard",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/LeaderboardEntry"
                                            }
                                        },
                                        "meta": {
                                            "type": "object",
                                            "properties": {
                                                "my_rank": {
                                                    "type": "integer",
                                                    "nullable": true
                                                },
                                                "total": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getChallengesByIdLeaderboard",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges/{id}/leave": {
            "post": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Leave a challenge",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Left"
                    }
                },
                "operationId": "postChallengesByIdLeave",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/challenges/{id}/progress": {
            "post": {
                "tags": [
                    "Challenges"
                ],
                "summary": "Refresh your progress",
                "description": "Recomputes your standing **from your own synced aggregates**. The app asks the server to refresh; it does not tell the server what the number is. That is what makes the leaderboard trustworthy — the same data that drives your dashboard drives your rank.\n\nCall this after a sync if you want the board to update immediately.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Progress refreshed"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postChallengesByIdProgress",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/config": {
            "get": {
                "tags": [
                    "Config"
                ],
                "summary": "Remote configuration",
                "description": "Feature flags, kill-switches, score weights, free/premium limits and experiment assignments.\n\nWorks **without a session** — a cold start needs flags before it has an account. Signed in, the\nresponse additionally reflects the caller's plan and their experiment variants.\n\n**Signed with ES256.** Verify `signature.token` against the public key shipped in the app before\napplying anything: without it, a hostile network could rewrite score weights or flip the AI\nkill-switches on the wire.\n\nCache for 60 seconds. A kill-switch has to take effect within about a minute, and this endpoint is\nhit on every cold start.\n\nExperiment assignment is **sticky**: it is a hash of the user id and experiment key, so a user lands\nin the same variant on every device and after every reinstall.\n\n**Rate limit:** 600 requests / 60s (bucket `global_ip`).",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Configuration",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "config": {
                                            "type": "object",
                                            "additionalProperties": true
                                        },
                                        "experiments": {
                                            "type": "object",
                                            "additionalProperties": {
                                                "type": "object",
                                                "properties": {
                                                    "variant": {
                                                        "type": "string"
                                                    }
                                                }
                                            }
                                        },
                                        "version": {
                                            "type": "integer"
                                        },
                                        "issued_at": {
                                            "type": "string",
                                            "format": "date-time"
                                        },
                                        "expires_at": {
                                            "type": "string",
                                            "format": "date-time"
                                        },
                                        "signature": {
                                            "type": "object",
                                            "nullable": true,
                                            "properties": {
                                                "algorithm": {
                                                    "type": "string",
                                                    "enum": [
                                                        "ES256"
                                                    ]
                                                },
                                                "token": {
                                                    "type": "string",
                                                    "description": "JWS whose `digest` claim is the SHA-256 of the canonical payload."
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "getConfig",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/config/keys": {
            "get": {
                "tags": [
                    "Config"
                ],
                "summary": "Chave pública e vetor de teste da assinatura",
                "description": "Tudo o que um cliente precisa para verificar a assinatura de `GET /v1/config`.\n\nA assinatura é feita sobre o corpo **canonicalizado pela RFC 8785** (JSON Canonicalization\nScheme), não sobre a saída bruta do servidor. Sem isso um cliente Swift não teria como\nreproduzir os mesmos bytes — o `json_encode` do PHP emite chaves na ordem de inserção — e\na assinatura seria impossível de conferir na prática.\n\n**Como verificar**\n\n1. Remova o objeto `signature` do corpo de `GET /v1/config`.\n2. Canonicalize o restante pela RFC 8785.\n3. Calcule SHA-256 desses bytes, em hexadecimal minúsculo.\n4. Verifique o JWS de `signature.token` com a chave cujo `kid` bate com o do cabeçalho.\n5. Confirme que a claim `digest` do JWS é igual ao hash calculado.\n6. Rejeite se `exp` já passou.\n\nO `test_vector` traz uma entrada com chaves fora de ordem, sua forma canônica e o digest\ncorrespondente. Se a sua implementação reproduz aquela string exata, ela está correta —\ne isso separa \"meu canonicalizador está errado\" de \"a assinatura é realmente inválida\",\nque é a diferença entre depurar e desistir da verificação.\n\nO `kid` é o thumbprint RFC 7638 da chave, então rotacionar é publicar uma segunda entrada,\nnão coordenar um release.\n\n**Rate limit:** 600 requests / 60s (bucket `global_ip`).",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Material de verificação",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "keys": [
                                            {
                                                "kty": "EC",
                                                "crv": "P-256",
                                                "x": "…",
                                                "y": "…",
                                                "alg": "ES256",
                                                "use": "sig",
                                                "kid": "…"
                                            }
                                        ],
                                        "verification": {
                                            "algorithm": "ES256",
                                            "canonicalization": "RFC 8785 (JSON Canonicalization Scheme)"
                                        },
                                        "test_vector": {
                                            "canonical_json": "{\"config\":{…},\"version\":7}",
                                            "canonical_sha256": "…"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Nenhuma chave de assinatura instalada neste servidor"
                    }
                },
                "operationId": "getConfigKeys",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/health": {
            "get": {
                "tags": [
                    "Health"
                ],
                "summary": "Liveness probe",
                "description": "Confirms the API process is serving. Does not touch the database — use `/health/ready` for that.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Service is up",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "example": "ok"
                                        },
                                        "service": {
                                            "type": "string",
                                            "example": "welleva-api"
                                        },
                                        "version": {
                                            "type": "string"
                                        },
                                        "time": {
                                            "type": "string",
                                            "format": "date-time"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "getHealth",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/health/ready": {
            "get": {
                "tags": [
                    "Health"
                ],
                "summary": "Readiness probe",
                "description": "Checks MariaDB, Redis and the analytics store. Returns **503** when any dependency is unreachable — a load balancer should drain the instance rather than restart it.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "All dependencies reachable"
                    },
                    "503": {
                        "description": "One or more dependencies are down"
                    }
                },
                "operationId": "getHealthReady",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/leagues/current": {
            "get": {
                "tags": [
                    "Leagues"
                ],
                "summary": "Current league and standings",
                "description": "Recomputes your points and returns your weekly cohort.\n\n**Leagues rank by consistency points, never by body metrics.** Ranking people by weight or body fat\nrewards being already-lean over improving, which is actively harmful in a wellness app — so the\nschema does not offer the columns to do it. Points come from logging, hitting your own goals,\nworkouts and mindful sessions, which puts a beginner and an athlete on the same board.\n\nCohorts hold up to 30 people. Top 7 promote, bottom 5 demote, and members with zero points neither\nrise nor fall — inactive accounts should not occupy promotion slots or pad the demotion zone.\n\nTiers: bronze → silver → gold → diamond.",
                "responses": {
                    "200": {
                        "description": "League standings",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "league": {
                                            "tier": "silver",
                                            "season_week": "2026-W35",
                                            "members": 28,
                                            "promote_top": 7,
                                            "demote_bottom": 5
                                        },
                                        "standings": [
                                            {
                                                "rank": 3,
                                                "user": {
                                                    "username": "ana.silva"
                                                },
                                                "points": 284,
                                                "is_you": true,
                                                "zone": "promotion"
                                            }
                                        ],
                                        "my_rank": 3,
                                        "my_points": 284
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getLeaguesCurrent",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/leagues/history": {
            "get": {
                "tags": [
                    "Leagues"
                ],
                "summary": "Past seasons",
                "description": "Up to 52 past weeks with rank and outcome, plus lifetime tier statistics.",
                "responses": {
                    "200": {
                        "description": "History"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getLeaguesHistory",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/leagues/standings": {
            "get": {
                "tags": [
                    "Leagues"
                ],
                "summary": "Standings without recomputing",
                "description": "Same shape as `/current`, but does not recompute points. Cheaper for polling.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Standings"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getLeaguesStandings",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/me": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Get your profile, goals, privacy settings and entitlement",
                "description": "The single call to make after sign-in: everything the app needs to render its first screen.",
                "responses": {
                    "200": {
                        "description": "Your account",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "user": {
                                                    "$ref": "#/components/schemas/User"
                                                },
                                                "profile": {
                                                    "type": "object"
                                                },
                                                "privacy": {
                                                    "type": "object"
                                                },
                                                "goals": {
                                                    "type": "object",
                                                    "additionalProperties": {
                                                        "$ref": "#/components/schemas/Goal"
                                                    }
                                                },
                                                "entitlement": {
                                                    "$ref": "#/components/schemas/Entitlement"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMe",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "patch": {
                "tags": [
                    "Profile"
                ],
                "summary": "Update account fields",
                "description": "Partial update. Only the fields you send are changed.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "display_name": {
                                        "type": "string",
                                        "nullable": true,
                                        "maxLength": 80
                                    },
                                    "locale": {
                                        "type": "string",
                                        "enum": [
                                            "pt-BR",
                                            "en",
                                            "es"
                                        ]
                                    },
                                    "timezone": {
                                        "type": "string",
                                        "description": "IANA identifier. Drives quiet hours and daily rollovers."
                                    },
                                    "unit_system": {
                                        "type": "string",
                                        "enum": [
                                            "metric",
                                            "imperial"
                                        ]
                                    },
                                    "birth_date": {
                                        "type": "string",
                                        "format": "date"
                                    },
                                    "sex": {
                                        "type": "string",
                                        "enum": [
                                            "female",
                                            "male",
                                            "other",
                                            "undisclosed"
                                        ]
                                    },
                                    "height_cm": {
                                        "type": "number",
                                        "minimum": 120,
                                        "maximum": 230
                                    },
                                    "country": {
                                        "type": "string",
                                        "minLength": 2,
                                        "maxLength": 2
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "patchMe",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/consents": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Current consents",
                "description": "LGPD/GDPR: which processing purposes the user has agreed to, and at which policy version.",
                "responses": {
                    "200": {
                        "description": "Consents"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMeConsents",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "post": {
                "tags": [
                    "Profile"
                ],
                "summary": "Grant or revoke a consent",
                "description": "Each processing purpose is consented to separately and can be revoked at any time.\n\n`ai_photo_processing` is **required** before `POST /v1/ai/food/photo` will run, and `nearby_location` before Nearby.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "consent_type",
                                    "granted",
                                    "version"
                                ],
                                "properties": {
                                    "consent_type": {
                                        "type": "string",
                                        "enum": [
                                            "terms",
                                            "privacy",
                                            "health_processing",
                                            "ai_photo_processing",
                                            "social_sharing",
                                            "nearby_location",
                                            "marketing_push",
                                            "analytics"
                                        ]
                                    },
                                    "granted": {
                                        "type": "boolean"
                                    },
                                    "version": {
                                        "type": "string",
                                        "example": "2026-01"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Recorded"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postMeConsents",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/goals": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Current active goals",
                "responses": {
                    "200": {
                        "description": "Goals keyed by metric"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMeGoals",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "put": {
                "tags": [
                    "Profile"
                ],
                "summary": "Override goals manually",
                "description": "Sets custom targets. Values outside physiological bounds are clamped rather than rejected.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "goals"
                                ],
                                "properties": {
                                    "goals": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "metric": {
                                                    "type": "string"
                                                },
                                                "target": {
                                                    "type": "number"
                                                },
                                                "unit": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "goals": [
                                    {
                                        "metric": "steps",
                                        "target": 12000
                                    },
                                    {
                                        "metric": "water_ml",
                                        "target": 3000
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Goals applied"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "putMeGoals",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/goals/{metric}/history": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Goal history for one metric",
                "description": "Every past target with its effective window — what makes a historic score explainable.",
                "parameters": [
                    {
                        "name": "metric",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "History"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMeGoalsByMetricHistory",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/me/onboarding": {
            "post": {
                "tags": [
                    "Profile"
                ],
                "summary": "Complete onboarding and generate the plan",
                "description": "Submits the onboarding quiz and returns a complete daily plan: calories, macros, water, steps, sleep,\nactive minutes and mindful minutes — each with a plain-language rationale you can show the user.\n\nCalories use Mifflin-St Jeor for BMR × an activity factor, then adjust for the goal. If you can supply\na measured 30-day TDEE from HealthKit in `measured_tdee`, it overrides the activity-factor estimate —\na measured number always beats a table lookup.\n\n**Safety rails are applied server-side and cannot be overridden:** the weight-change pace is capped at\n1.0 kg/week, and the calorie target never drops below 1,200 kcal (female) / 1,500 kcal (male),\nwhatever was requested.\n\nGoals are versioned by effective date rather than overwritten, so a past score stays explainable.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "goal_type",
                                    "sex",
                                    "birth_date",
                                    "height_cm",
                                    "current_weight_kg",
                                    "activity_level"
                                ],
                                "properties": {
                                    "goal_type": {
                                        "type": "string",
                                        "enum": [
                                            "lose_weight",
                                            "gain_muscle",
                                            "sleep_better",
                                            "more_energy",
                                            "general_health",
                                            "maintain"
                                        ]
                                    },
                                    "sex": {
                                        "type": "string",
                                        "enum": [
                                            "female",
                                            "male",
                                            "other",
                                            "undisclosed"
                                        ]
                                    },
                                    "birth_date": {
                                        "type": "string",
                                        "format": "date"
                                    },
                                    "height_cm": {
                                        "type": "number",
                                        "minimum": 120,
                                        "maximum": 230
                                    },
                                    "current_weight_kg": {
                                        "type": "number",
                                        "minimum": 35,
                                        "maximum": 300
                                    },
                                    "target_weight_kg": {
                                        "type": "number",
                                        "nullable": true
                                    },
                                    "weight_pace_kg_week": {
                                        "type": "number",
                                        "minimum": 0.1,
                                        "maximum": 1,
                                        "default": 0.5
                                    },
                                    "activity_level": {
                                        "type": "string",
                                        "enum": [
                                            "sedentary",
                                            "light",
                                            "moderate",
                                            "active",
                                            "very_active"
                                        ]
                                    },
                                    "dietary_pattern": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "dietary_restrictions": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        }
                                    },
                                    "measured_tdee": {
                                        "type": "number",
                                        "nullable": true,
                                        "description": "Measured daily energy expenditure from HealthKit. Overrides the estimate."
                                    },
                                    "baseline_sleep_minutes": {
                                        "type": "integer",
                                        "nullable": true
                                    },
                                    "baseline_water_ml": {
                                        "type": "integer",
                                        "nullable": true
                                    },
                                    "caffeine_cups_per_day": {
                                        "type": "integer",
                                        "nullable": true
                                    }
                                }
                            },
                            "example": {
                                "goal_type": "lose_weight",
                                "sex": "female",
                                "birth_date": "1990-05-14",
                                "height_cm": 168,
                                "current_weight_kg": 74.5,
                                "target_weight_kg": 66,
                                "weight_pace_kg_week": 0.5,
                                "activity_level": "moderate",
                                "dietary_restrictions": [
                                    "lactose"
                                ],
                                "caffeine_cups_per_day": 3
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Plan generated",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "plan": {
                                                    "type": "object",
                                                    "additionalProperties": {
                                                        "type": "object",
                                                        "properties": {
                                                            "value": {
                                                                "type": "number"
                                                            },
                                                            "unit": {
                                                                "type": "string"
                                                            },
                                                            "rationale": {
                                                                "type": "string"
                                                            }
                                                        }
                                                    }
                                                },
                                                "goals": {
                                                    "type": "object",
                                                    "additionalProperties": {
                                                        "$ref": "#/components/schemas/Goal"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "data": {
                                        "plan": {
                                            "calories": {
                                                "value": 1810,
                                                "unit": "kcal",
                                                "rationale": "TDEE ≈ 2360 kcal (estimated) − 550 kcal for 0.5 kg/week"
                                            },
                                            "protein_g": {
                                                "value": 134,
                                                "unit": "g",
                                                "rationale": "1.8 g per kg of body weight"
                                            },
                                            "water_ml": {
                                                "value": 2900,
                                                "unit": "ml",
                                                "rationale": "35 ml per kg, adjusted for your activity level"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postMeOnboarding",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/privacy": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Privacy settings",
                "responses": {
                    "200": {
                        "description": "Settings"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMePrivacy",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "patch": {
                "tags": [
                    "Profile"
                ],
                "summary": "Update privacy settings",
                "description": "Controls what friends can see. **Everything defaults to off or friends-only** — sharing is an explicit act.\n\n`leaderboard_visibility: anonymous` keeps the user competing while hiding their identity; `hidden` removes them from boards entirely.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "share_scores": {
                                        "type": "boolean"
                                    },
                                    "share_streaks": {
                                        "type": "boolean"
                                    },
                                    "share_activity": {
                                        "type": "boolean"
                                    },
                                    "share_nutrition": {
                                        "type": "boolean"
                                    },
                                    "share_weight": {
                                        "type": "boolean"
                                    },
                                    "discoverable_by_username": {
                                        "type": "boolean"
                                    },
                                    "nearby_enabled": {
                                        "type": "boolean"
                                    },
                                    "feed_visibility": {
                                        "type": "string",
                                        "enum": [
                                            "friends",
                                            "none"
                                        ]
                                    },
                                    "leaderboard_visibility": {
                                        "type": "string",
                                        "enum": [
                                            "username",
                                            "anonymous",
                                            "hidden"
                                        ]
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "patchMePrivacy",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/profile": {
            "patch": {
                "tags": [
                    "Profile"
                ],
                "summary": "Update wellness profile",
                "description": "Goal type, activity level, dietary pattern, habits and routine. Feeds goal calculation.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "goal_type": {
                                        "type": "string",
                                        "enum": [
                                            "lose_weight",
                                            "gain_muscle",
                                            "sleep_better",
                                            "more_energy",
                                            "general_health",
                                            "maintain"
                                        ]
                                    },
                                    "activity_level": {
                                        "type": "string",
                                        "enum": [
                                            "sedentary",
                                            "light",
                                            "moderate",
                                            "active",
                                            "very_active"
                                        ]
                                    },
                                    "target_weight_kg": {
                                        "type": "number"
                                    },
                                    "weight_pace_kg_week": {
                                        "type": "number",
                                        "minimum": 0.1,
                                        "maximum": 1
                                    },
                                    "dietary_pattern": {
                                        "type": "string",
                                        "example": "low_carb"
                                    },
                                    "dietary_restrictions": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        }
                                    },
                                    "usual_wake_time": {
                                        "type": "string",
                                        "example": "06:30"
                                    },
                                    "usual_bed_time": {
                                        "type": "string",
                                        "example": "23:00"
                                    },
                                    "caffeine_cups_per_day": {
                                        "type": "integer"
                                    },
                                    "alcohol_units_per_week": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "patchMeProfile",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/me/quotas": {
            "get": {
                "tags": [
                    "Profile"
                ],
                "summary": "Remaining AI quota",
                "description": "Lets the app render \"2 of 3 analyses left this week\" rather than letting a failed request be how the user discovers a limit.\n\nFree is a **weekly** allowance; Premium is a **daily** fair-use ceiling.",
                "responses": {
                    "200": {
                        "description": "Quota state",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "plan": "free",
                                        "quotas": {
                                            "ai_photo": {
                                                "used": 1,
                                                "limit": 3,
                                                "remaining": 2,
                                                "resets_at": "2026-09-01T00:00:00+00:00",
                                                "period": "week"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getMeQuotas",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/nearby/checkin": {
            "post": {
                "tags": [
                    "Nearby"
                ],
                "summary": "Check in to an approximate area",
                "description": "Makes you discoverable to nearby users who want to walk, run or train together.\n\n**Coordinates are never stored.** They are converted to a geohash at the moment of receipt and\ndiscarded: precision 6 (~1.2 km) is kept internally, precision 5 (~4.9 km) is what discovery matches\non. That is enough to suggest a walking partner and useless for locating an individual.\n\nThere is **one active check-in per user**, so no location trail can accumulate, and it expires\nautomatically (4 h default, 24 h maximum).\n\nRequires all of: Premium, `nearby_enabled` in privacy settings, `nearby_location` consent, and an age\nof 17+ derived from `birth_date`. Each is a separate gate with its own error code.\n\n**Rate limit:** 12 requests / 3600s (bucket `nearby_checkin`).\n\n**Consent required:** `nearby_location` must be granted via `POST /v1/me/consents`.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "latitude",
                                    "longitude",
                                    "activity"
                                ],
                                "properties": {
                                    "latitude": {
                                        "type": "number",
                                        "minimum": -90,
                                        "maximum": 90
                                    },
                                    "longitude": {
                                        "type": "number",
                                        "minimum": -180,
                                        "maximum": 180
                                    },
                                    "activity": {
                                        "type": "string",
                                        "enum": [
                                            "walk",
                                            "run",
                                            "gym",
                                            "yoga",
                                            "cycling",
                                            "other"
                                        ]
                                    },
                                    "note": {
                                        "type": "string",
                                        "nullable": true,
                                        "maxLength": 140
                                    },
                                    "ttl_hours": {
                                        "type": "integer",
                                        "minimum": 1,
                                        "maximum": 24,
                                        "default": 4
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Checked in",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "checked_in": true,
                                        "activity": "run",
                                        "area": "6gycf",
                                        "precision": "approximately 5 km",
                                        "expires_at": "2026-08-28T15:00:00+00:00"
                                    }
                                }
                            }
                        }
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "description": "Opt-in, consent or age requirement not met",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postNearbyCheckin",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "delete": {
                "tags": [
                    "Nearby"
                ],
                "summary": "Check out",
                "description": "Removes your check-in immediately, ahead of its expiry.",
                "responses": {
                    "204": {
                        "description": "Checked out"
                    }
                },
                "operationId": "deleteNearbyCheckin",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/nearby/discover": {
            "get": {
                "tags": [
                    "Nearby"
                ],
                "summary": "Find nearby users",
                "description": "Searches your geohash cell plus its eight neighbours — two people 100 m apart can sit either side of a cell boundary, and searching only the exact cell would hide them from each other.\n\nResults carry a coarse distance **band** (`very_close` / `close` / `nearby` / `in_your_area`), never a figure in metres: an exact distance from several vantage points would trilaterate someone's position.",
                "parameters": [
                    {
                        "name": "activity",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "walk",
                                "run",
                                "gym",
                                "yoga",
                                "cycling",
                                "other"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Nearby users"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "description": "Not checked in, or requirements not met",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "getNearbyDiscover",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/privacy/account": {
            "delete": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Schedule account deletion",
                "description": "LGPD art. 18 §VI. Two-phase by design.\n\n**Now:** the account is marked pending deletion and every session is revoked — the account stops\nworking the moment deletion is requested, not when it executes.\n\n**After the grace window (14 days):** personal data is deleted outright and the account row is\nanonymised. Anonymising rather than dropping keeps aggregate statistics and legally-retained billing\nrecords valid while removing every identifier. There is no path back from a completed deletion.\n\nRequires `confirm: true`. An irreversible action should need an explicit acknowledgement, not just\nthe right HTTP verb.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "confirm"
                                ],
                                "properties": {
                                    "confirm": {
                                        "type": "boolean",
                                        "enum": [
                                            true
                                        ]
                                    },
                                    "reason": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Deletion scheduled"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "deletePrivacyAccount",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/privacy/account/cancel-deletion": {
            "post": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Cancel a scheduled deletion",
                "description": "Reactivates the account within the grace window. Requires signing in again, since sessions were revoked.",
                "responses": {
                    "200": {
                        "description": "Cancelled"
                    },
                    "409": {
                        "description": "Nothing scheduled",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "operationId": "postPrivacyAccountCanceldeletion",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/privacy/account/deletion-status": {
            "get": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Deletion status",
                "responses": {
                    "200": {
                        "description": "Status, or `{\"status\":\"none\"}`"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getPrivacyAccountDeletionstatus",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/privacy/export": {
            "post": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Request a data export",
                "description": "LGPD art. 18 / GDPR art. 15. Builds a complete machine-readable archive of everything the server holds.\n\nBuilt asynchronously; poll `GET /v1/privacy/export/{id}`. The archive expires after 7 days. Limited to 3 requests per day.\n\n**Rate limit:** 3 requests / 86400s (bucket `export_request`).",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "format": {
                                        "type": "string",
                                        "enum": [
                                            "json",
                                            "csv"
                                        ],
                                        "default": "json"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Export queued"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postPrivacyExport",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/privacy/export/{id}": {
            "get": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Export status",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Status"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getPrivacyExportById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/privacy/export/{id}/download": {
            "get": {
                "tags": [
                    "Privacy"
                ],
                "summary": "Download an export",
                "description": "Requires the one-time `token` issued when the export completed. Returns the archive as a file attachment.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The archive",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not ready, expired, or the token does not match",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "operationId": "getPrivacyExportByIdDownload",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/privacy/summary": {
            "get": {
                "tags": [
                    "Privacy"
                ],
                "summary": "What is stored, and what is not",
                "description": "A plain-language statement for the in-app privacy screen. An endpoint rather than a static page, so the app shows the real answer for this build rather than a generic policy.",
                "responses": {
                    "200": {
                        "description": "Privacy summary"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getPrivacySummary",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/push/opened": {
            "post": {
                "tags": [
                    "Push"
                ],
                "summary": "Report a notification open",
                "description": "Attribution for notification effectiveness. Call from the notification-response handler.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "rule_key"
                                ],
                                "properties": {
                                    "rule_key": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "204": {
                        "description": "Recorded"
                    }
                },
                "operationId": "postPushOpened",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/push/preferences": {
            "get": {
                "tags": [
                    "Push"
                ],
                "summary": "Notification preferences",
                "description": "Per-category toggles and quiet hours. Quiet hours are evaluated in the **user's** timezone.",
                "responses": {
                    "200": {
                        "description": "Preferences"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getPushPreferences",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "patch": {
                "tags": [
                    "Push"
                ],
                "summary": "Update notification preferences",
                "description": "Categories: `functional` (reminders the user asked for), `engagement`, `conversion`, `social`, `system`.\n\nWindows crossing midnight (22:00–07:30) are handled correctly. Getting this wrong is how a wellness app wakes someone at 3 a.m.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "preferences"
                                ],
                                "properties": {
                                    "preferences": {
                                        "type": "object",
                                        "additionalProperties": {
                                            "type": "object",
                                            "properties": {
                                                "enabled": {
                                                    "type": "boolean"
                                                },
                                                "quiet_start": {
                                                    "type": "string"
                                                },
                                                "quiet_end": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "preferences": {
                                    "conversion": {
                                        "enabled": false
                                    },
                                    "functional": {
                                        "quiet_start": "23:00",
                                        "quiet_end": "06:30"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Updated"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "patchPushPreferences",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/push/token": {
            "put": {
                "tags": [
                    "Push"
                ],
                "summary": "Register an APNs device token",
                "description": "Call on every launch — APNs tokens rotate. A token APNs later reports as unregistered is forgotten automatically.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "device_uuid",
                                    "push_token"
                                ],
                                "properties": {
                                    "device_uuid": {
                                        "type": "string"
                                    },
                                    "push_token": {
                                        "type": "string",
                                        "pattern": "^[a-fA-F0-9]+$"
                                    },
                                    "environment": {
                                        "type": "string",
                                        "enum": [
                                            "sandbox",
                                            "production"
                                        ],
                                        "default": "production"
                                    },
                                    "platform": {
                                        "type": "string",
                                        "enum": [
                                            "ios",
                                            "watchos",
                                            "ipados"
                                        ]
                                    },
                                    "app_version": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "os_version": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "model": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Registered"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "putPushToken",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "delete": {
                "tags": [
                    "Push"
                ],
                "summary": "Unregister a device token",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "device_uuid"
                                ],
                                "properties": {
                                    "device_uuid": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "204": {
                        "description": "Unregistered"
                    }
                },
                "operationId": "deletePushToken",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/social/blocks": {
            "get": {
                "tags": [
                    "Social"
                ],
                "summary": "List blocked users",
                "responses": {
                    "200": {
                        "description": "Blocked users"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSocialBlocks",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "Block a user",
                "description": "Blocking is symmetric for visibility and **severs any existing friendship** and pending request in the same operation.\n\n**Rate limit:** 60 requests / 300s (bucket `social_write`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "user_id"
                                ],
                                "properties": {
                                    "user_id": {
                                        "type": "string"
                                    },
                                    "reason": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Blocked"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSocialBlocks",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/social/blocks/{id}": {
            "delete": {
                "tags": [
                    "Social"
                ],
                "summary": "Unblock a user",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Unblocked"
                    }
                },
                "operationId": "deleteSocialBlocksById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/feed": {
            "get": {
                "tags": [
                    "Social"
                ],
                "summary": "Friend achievement feed",
                "description": "Cursor-paginated, newest first. Blocked users are filtered in both directions.\n\nReactions only, no free-text comments in v1 — a closed reaction set cannot carry harassment, which is what makes social features safe to launch with a small team.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Cursor"
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Feed page",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "type": "object"
                                            }
                                        },
                                        "meta": {
                                            "type": "object",
                                            "properties": {
                                                "next_cursor": {
                                                    "type": "string",
                                                    "nullable": true
                                                },
                                                "has_more": {
                                                    "type": "boolean"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSocialFeed",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/feed/{id}": {
            "delete": {
                "tags": [
                    "Social"
                ],
                "summary": "Delete your own post",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Deleted"
                    }
                },
                "operationId": "deleteSocialFeedById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/feed/{id}/react": {
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "React to a post",
                "description": "Sending the same reaction again removes it (toggle). A different one replaces it.\n\n**Rate limit:** 60 requests / 300s (bucket `social_write`).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "reaction"
                                ],
                                "properties": {
                                    "reaction": {
                                        "type": "string",
                                        "enum": [
                                            "clap",
                                            "fire",
                                            "strong",
                                            "heart",
                                            "wow"
                                        ]
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Reaction applied"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSocialFeedByIdReact",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/friends": {
            "get": {
                "tags": [
                    "Social"
                ],
                "summary": "List friends",
                "description": "Each friend's shared metrics honour **their** privacy settings, applied in SQL — a metric they have not chosen to share is simply absent from `shared`, never null-but-present.",
                "responses": {
                    "200": {
                        "description": "Friends",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": [
                                        {
                                            "id": "usr_m8k…",
                                            "username": "joao.p",
                                            "display_name": "João P.",
                                            "friends_since": "2026-06-14T10:22:00Z",
                                            "shared": {
                                                "steps": 9840,
                                                "streak": 22
                                            }
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSocialFriends",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "Send a friend request",
                "description": "If the other person already has a pending request to you, this **accepts immediately** rather than creating a second one — both people clearly want it.\n\nA user who has turned off `discoverable_by_username` returns the same `404 user_not_found` as a user who does not exist. Two different responses would make the setting probeable.\n\n**Rate limit:** 30 requests / 3600s (bucket `friend_request`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "username"
                                ],
                                "properties": {
                                    "username": {
                                        "type": "string"
                                    },
                                    "source": {
                                        "type": "string",
                                        "enum": [
                                            "username",
                                            "qr",
                                            "contacts",
                                            "nearby",
                                            "challenge"
                                        ],
                                        "default": "username"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Request sent, or accepted if mutual"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "409": {
                        "$ref": "#/components/responses/Conflict"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSocialFriends",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/social/friends/requests": {
            "get": {
                "tags": [
                    "Social"
                ],
                "summary": "Pending friend requests",
                "responses": {
                    "200": {
                        "description": "Incoming and outgoing requests"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSocialFriendsRequests",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/social/friends/requests/{id}/accept": {
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "Accept a friend request",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Accepted"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "409": {
                        "$ref": "#/components/responses/Conflict"
                    }
                },
                "operationId": "postSocialFriendsRequestsByIdAccept",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/friends/requests/{id}/decline": {
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "Decline a friend request",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Declined"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "postSocialFriendsRequestsByIdDecline",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/friends/{id}": {
            "delete": {
                "tags": [
                    "Social"
                ],
                "summary": "Remove a friend",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Removed"
                    }
                },
                "operationId": "deleteSocialFriendsById",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/social/reports": {
            "post": {
                "tags": [
                    "Social"
                ],
                "summary": "Report a user",
                "description": "Queues the report for moderation. Rate limited to 10 per day to keep reporting from becoming a harassment vector itself.\n\n**Rate limit:** 10 requests / 86400s (bucket `report_user`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "user_id",
                                    "context",
                                    "reason"
                                ],
                                "properties": {
                                    "user_id": {
                                        "type": "string"
                                    },
                                    "context": {
                                        "type": "string",
                                        "enum": [
                                            "profile",
                                            "nearby",
                                            "challenge",
                                            "feed",
                                            "message"
                                        ]
                                    },
                                    "reason": {
                                        "type": "string",
                                        "enum": [
                                            "harassment",
                                            "spam",
                                            "impersonation",
                                            "inappropriate",
                                            "underage",
                                            "other"
                                        ]
                                    },
                                    "details": {
                                        "type": "string",
                                        "nullable": true,
                                        "maxLength": 1000
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Report filed"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSocialReports",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/social/users/{username}": {
            "get": {
                "tags": [
                    "Social"
                ],
                "summary": "Public profile",
                "description": "A blocked user is indistinguishable from a non-existent one: both return 404.",
                "parameters": [
                    {
                        "name": "username",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Profile"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    }
                },
                "operationId": "getSocialUsersByUsername",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/sync/aggregates": {
            "post": {
                "tags": [
                    "Sync"
                ],
                "summary": "Upload daily aggregates",
                "description": "Uploads one row per day of totals. Up to 120 days per request.\n\n**This endpoint accepts daily aggregates only.** Raw HealthKit samples — heart-rate series, individual\nsleep sessions, workout GPS routes — must never be sent, and the server ignores any field outside its\nallow-list rather than storing it. What is stored exists to serve challenges, leagues, Wrapped\npercentiles and cross-device continuity; nothing else needs a server.\n\n**Partial updates are safe.** Only the keys present in each day are written, so a device syncing\nnutrition will not blank out sleep data another device wrote.\n\n**Values outside physiological bounds are dropped, not clamped.** 9,999,999 steps or a resting heart\nrate of 300 is a HealthKit glitch or a unit mix-up, and silently correcting it would put a fictional\nnumber on a leaderboard.\n\nStreaks are recomputed on every accepted upload, so a watch uploading Tuesday's data on Thursday still\nextends Tuesday's streak.\n\n**Rate limit:** 60 requests / 300s (bucket `sync_upload`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "days"
                                ],
                                "properties": {
                                    "days": {
                                        "type": "array",
                                        "maxItems": 120,
                                        "items": {
                                            "$ref": "#/components/schemas/DailyAggregate"
                                        }
                                    },
                                    "device_uuid": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            },
                            "example": {
                                "device_uuid": "B8F3…",
                                "days": [
                                    {
                                        "day": "2026-08-27",
                                        "steps": 11420,
                                        "active_kcal": 612,
                                        "exercise_minutes": 47,
                                        "sleep_minutes": 445,
                                        "sleep_deep_min": 78,
                                        "sleep_rem_min": 96,
                                        "resting_hr": 54,
                                        "hrv_ms": 62.4,
                                        "weight_kg": 73.8,
                                        "kcal_in": 2080,
                                        "protein_g": 138,
                                        "water_ml": 2600,
                                        "meals_logged": 4,
                                        "daily_score": 84,
                                        "sleep_score": 79
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Accepted",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "accepted": 1,
                                        "days": [
                                            "2026-08-27"
                                        ],
                                        "rejected": [
                                            {
                                                "day": "2026-09-05",
                                                "reason": "future_date"
                                            }
                                        ],
                                        "streaks": [
                                            {
                                                "streak_type": "all_goals",
                                                "current_length": 12,
                                                "longest_length": 31
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSyncAggregates",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            },
            "get": {
                "tags": [
                    "Sync"
                ],
                "summary": "Download aggregates for a range",
                "description": "Restores history to a reinstalled or second device. Maximum 400 days per request.",
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Aggregates",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/DailyAggregate"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "getSyncAggregates",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/sync/resources": {
            "get": {
                "tags": [
                    "Sync"
                ],
                "summary": "Recursos sincronizáveis e o protocolo",
                "description": "Lista os domínios que o app pode espelhar na conta, com os campos aceitos por cada um.\n\nServe para o cliente descobrir a superfície em vez de embutir uma lista que envelhece silenciosamente quando o servidor muda.",
                "responses": {
                    "200": {
                        "description": "Recursos disponíveis"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSyncResources",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/sync/status": {
            "get": {
                "tags": [
                    "Sync"
                ],
                "summary": "Sync state and insight readiness",
                "description": "Reports coverage and how close the account is to the 30 days the Cause & Effect engine needs.\n\n`insights_readiness.progress` is what drives the \"12/30 days collected\" cold-start card — it turns a two-week wait into visible progress instead of an empty screen.",
                "responses": {
                    "200": {
                        "description": "Status"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSyncStatus",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/sync/summary": {
            "get": {
                "tags": [
                    "Sync"
                ],
                "summary": "Trends over a window",
                "description": "Averages for the last N days against the *same-length preceding window*, so the comparison is like-for-like. Direction is reported as up/down/flat with a 3% dead band, which keeps day-to-day noise from reading as a trend.",
                "parameters": [
                    {
                        "name": "days",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 365,
                            "default": 7
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Trends"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getSyncSummary",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/sync/{resource}": {
            "post": {
                "tags": [
                    "Sync"
                ],
                "summary": "Enviar registros de um recurso",
                "description": "Envia um lote de registros que o aparelho criou ou alterou. Até 500 por requisição.\n\n**Idempotência.** A chave é `usuário + id`. Reenviar o mesmo `id` atualiza o mesmo\nregistro; nunca cria um segundo. Um retry depois de uma resposta perdida é seguro por\nconstrução, o que em rede móvel é requisito e não otimização.\n\n**Conflito.** Vence quem tem o `updated_at` mais recente — o relógio do **cliente**, porque\num aparelho que ficou offline precisa poder trazer trabalho mais novo. Quando o servidor\njá tem uma versão igual ou mais nova, o registro volta em `skipped`: isso é o sinal para\no cliente fazer um pull, não para reenviar.\n\n**Exclusão.** Mande `deleted_at`. O registro permanece como marcador para que a exclusão\nchegue aos outros aparelhos; um `DELETE` de verdade seria invisível para um aparelho que\nainda não sincronizou, e o registro ressuscitaria no próximo push dele.\n\n**Recursos com chave por dia** (`sleep-nights`, `readiness-daily`) derivam a identidade do\npróprio campo `day`. Dois aparelhos que calcularam a mesma noite convergem numa linha só,\nem vez de duplicá-la.\n\nUm registro inválido é rejeitado individualmente e devolvido em `rejected` com os campos\nque falharam — o resto do lote é aplicado normalmente.\n\n**Rate limit:** 60 requests / 300s (bucket `sync_upload`).",
                "parameters": [
                    {
                        "name": "resource",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "fast-sessions",
                                "medication-plans",
                                "medication-logs",
                                "sleep-nights",
                                "smart-alarm-sessions",
                                "lucid-coach-sessions",
                                "readiness-daily",
                                "mindfulness-logs"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "records"
                                ],
                                "properties": {
                                    "records": {
                                        "type": "array",
                                        "maxItems": 500,
                                        "items": {
                                            "type": "object",
                                            "required": [
                                                "id",
                                                "updated_at"
                                            ],
                                            "properties": {
                                                "id": {
                                                    "type": "string",
                                                    "maxLength": 64,
                                                    "description": "Identificador gerado pelo cliente."
                                                },
                                                "updated_at": {
                                                    "type": "string",
                                                    "format": "date-time",
                                                    "description": "Quando o cliente editou. Resolve conflito."
                                                },
                                                "deleted_at": {
                                                    "type": "string",
                                                    "format": "date-time",
                                                    "nullable": true
                                                }
                                            },
                                            "additionalProperties": true
                                        }
                                    }
                                }
                            },
                            "example": {
                                "records": [
                                    {
                                        "id": "B8F3E1A0-4C2D-4E1F-9A3B-7C5D2E8F1A6B",
                                        "started_at": "2026-08-27T21:00:00.000Z",
                                        "ended_at": "2026-08-28T13:00:00.000Z",
                                        "target_hours": 16,
                                        "updated_at": "2026-08-28T13:00:04.512Z"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Lote processado",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "applied": 3,
                                        "skipped": 1,
                                        "rejected": [
                                            {
                                                "id": "…",
                                                "reason": "invalid",
                                                "fields": {
                                                    "status": [
                                                        "…"
                                                    ]
                                                }
                                            }
                                        ]
                                    },
                                    "meta": {
                                        "server_time": "2026-08-28T13:00:05.117+00:00"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recurso desconhecido"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postSyncByResource",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            },
            "get": {
                "tags": [
                    "Sync"
                ],
                "summary": "Buscar alterações de um recurso",
                "description": "Devolve tudo que mudou depois do cursor, incluindo exclusões.\n\nPasse `meta.next_cursor` da resposta anterior em `since`. O cursor vem do relógio do\n**servidor**, com precisão de milissegundo — de propósito. Se ele usasse o `updated_at`\ndo cliente, um aparelho com o relógio errado gravaria registros que ordenam antes do\ncursor e ficariam invisíveis para sempre; e se fosse truncado ao segundo, o cliente\nrebaixaria o mesmo registro em todo poll sem nunca convergir.\n\nRegistros excluídos aparecem com `deleted_at` preenchido em vez de sumirem, para que a\nexclusão seja aplicada localmente.\n\nNa primeira sincronização, omita `since` para receber tudo.",
                "parameters": [
                    {
                        "name": "resource",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "fast-sessions",
                                "medication-plans",
                                "medication-logs",
                                "sleep-nights",
                                "smart-alarm-sessions",
                                "lucid-coach-sessions",
                                "readiness-daily",
                                "mindfulness-logs"
                            ]
                        }
                    },
                    {
                        "name": "since",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "description": "Cursor da resposta anterior. Omitir traz tudo."
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 500,
                            "default": 200
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Alterações",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": [
                                        {
                                            "id": "B8F3E1A0-…",
                                            "started_at": "2026-08-27T21:00:00.000+00:00",
                                            "target_hours": 16,
                                            "updated_at": "2026-08-28T13:00:04.512+00:00",
                                            "deleted_at": null
                                        }
                                    ],
                                    "meta": {
                                        "next_cursor": "2026-08-28T13:00:05.117+00:00",
                                        "has_more": false,
                                        "count": 1
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Recurso desconhecido"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "getSyncByResource",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        },
        "/v1/telemetry/events": {
            "post": {
                "tags": [
                    "Telemetry"
                ],
                "summary": "Send a batch of product events",
                "description": "Batched analytics. Up to 100 events per request; works with or without a session.\n\nTwo rules keep this from becoming a privacy problem. Event names are **allow-listed** — an unknown\nname is dropped and reported back in `meta.unknown_events`, so a client bug is debuggable rather than\nsilently losing a release's analytics. And properties are **scalars only**, capped at 20 keys and 200\ncharacters, so health data cannot be smuggled in as an \"event property\".\n\nClient clocks drift, so `occurred_at` is clamped: anything more than 7 days old or an hour in the\nfuture is replaced with server time.\n\n**Rate limit:** 30 requests / 60s (bucket `telemetry`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "events"
                                ],
                                "properties": {
                                    "events": {
                                        "type": "array",
                                        "maxItems": 100,
                                        "items": {
                                            "type": "object",
                                            "required": [
                                                "name"
                                            ],
                                            "properties": {
                                                "name": {
                                                    "type": "string"
                                                },
                                                "properties": {
                                                    "type": "object",
                                                    "additionalProperties": {
                                                        "oneOf": [
                                                            {
                                                                "type": "string"
                                                            },
                                                            {
                                                                "type": "number"
                                                            },
                                                            {
                                                                "type": "boolean"
                                                            }
                                                        ]
                                                    }
                                                },
                                                "occurred_at": {
                                                    "type": "string",
                                                    "format": "date-time"
                                                }
                                            }
                                        }
                                    },
                                    "session_id": {
                                        "type": "string"
                                    },
                                    "device_id": {
                                        "type": "string"
                                    },
                                    "app_version": {
                                        "type": "string"
                                    },
                                    "os_version": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "session_id": "s_9f2a",
                                "app_version": "1.0.0",
                                "events": [
                                    {
                                        "name": "paywall_viewed",
                                        "properties": {
                                            "variant": "annual_highlight",
                                            "trigger": "ai_limit"
                                        }
                                    },
                                    {
                                        "name": "meal_logged",
                                        "properties": {
                                            "method": "photo"
                                        }
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Accepted",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "accepted": 2,
                                        "dropped": 0,
                                        "unknown_events": []
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postTelemetryEvents",
                "security": [],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/waitlist": {
            "post": {
                "tags": [
                    "Marketing"
                ],
                "summary": "Join the pre-launch waitlist",
                "description": "Captures an email from the marketing site before the App Store listing is live.\n\nUnauthenticated by necessity — the visitor has no account yet. Three defences stand in for auth: a\nhoneypot field (`website`, rendered off-screen), a minimum time-on-form (`elapsed`, since bots submit\ninstantly), and per-IP rate limiting.\n\n**Re-submitting an address returns the same success response as a first signup.** Answering \"that\nemail is already registered\" would turn this into a membership oracle for any address someone cares\nto try.\n\n**Rate limit:** 8 requests / 3600s (bucket `waitlist`).",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "email"
                                ],
                                "properties": {
                                    "email": {
                                        "type": "string",
                                        "format": "email"
                                    },
                                    "name": {
                                        "type": "string",
                                        "nullable": true,
                                        "maxLength": 80
                                    },
                                    "goal": {
                                        "type": "string",
                                        "nullable": true,
                                        "enum": [
                                            "lose_weight",
                                            "gain_muscle",
                                            "sleep_better",
                                            "more_energy",
                                            "general_health"
                                        ]
                                    },
                                    "locale": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "website": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Honeypot. Must stay empty."
                                    },
                                    "elapsed": {
                                        "type": "integer",
                                        "nullable": true,
                                        "description": "Seconds the form was on screen."
                                    },
                                    "utm_source": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "utm_medium": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "utm_campaign": {
                                        "type": "string",
                                        "nullable": true
                                    }
                                }
                            },
                            "example": {
                                "email": "ana@exemplo.com",
                                "name": "Ana",
                                "goal": "sleep_better",
                                "elapsed": 14
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "On the list",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "joined": true,
                                        "position": 428,
                                        "message": "Você está na lista."
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                },
                "operationId": "postWaitlist",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/waitlist/count": {
            "get": {
                "tags": [
                    "Marketing"
                ],
                "summary": "Waitlist size",
                "description": "Public count for social proof on the landing page. Cached for 5 minutes.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Current count",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "count": 428
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "getWaitlistCount",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/wrapped": {
            "get": {
                "tags": [
                    "Wrapped"
                ],
                "summary": "Available Wrapped periods",
                "responses": {
                    "200": {
                        "description": "Periods"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                },
                "operationId": "getWrapped",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ]
            }
        },
        "/v1/wrapped/{period}": {
            "get": {
                "tags": [
                    "Wrapped"
                ],
                "summary": "Get a Wrapped report",
                "description": "`period` is `2026` for a year or `2026-08` for a month.\n\nThe backend returns numbers and percentiles; **the app renders the cards natively**, which is what\nkeeps them fast and shareable at full resolution.\n\nPercentiles come from pre-computed population cut-points, so \"you walked more than 78% of users\" is a\nsingle lookup that never touches another user's row. A percentile is withheld entirely when the\nsample is under 100 people — a rank drawn from a handful of users is noise, and can be\nde-anonymising in a small cohort.\n\nFree accounts receive the headline numbers with a watermark; Premium receives the full set,\npercentiles and theme options.\n\nReturns **404 `wrapped_insufficient_data`** below 10 days (month) or 60 days (year) of data.",
                "parameters": [
                    {
                        "name": "period",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "example": "2026-08"
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Locale"
                    },
                    {
                        "$ref": "#/components/parameters/AppVersion"
                    },
                    {
                        "$ref": "#/components/parameters/RequestId"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Wrapped report",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                },
                                "example": {
                                    "data": {
                                        "period_type": "month",
                                        "period_key": "2026-08",
                                        "days_covered": 28,
                                        "is_full": true,
                                        "stats": {
                                            "total_steps": 284120,
                                            "total_distance_km": 213.4,
                                            "workouts": 18,
                                            "water_litres": 71.2,
                                            "longest_streak": 22,
                                            "avg_sleep_hours": 7.3,
                                            "fun_facts": [
                                                {
                                                    "key": "distance",
                                                    "value": 213.4,
                                                    "unit": "km",
                                                    "equivalent": "5.1 maratonas"
                                                }
                                            ]
                                        },
                                        "percentiles": {
                                            "steps": {
                                                "value": 284120,
                                                "percentile": 78,
                                                "better_than": "78%"
                                            }
                                        },
                                        "shareable": {
                                            "formats": [
                                                "story_9_16",
                                                "square_1_1"
                                            ],
                                            "watermark": false,
                                            "themes": [
                                                "dark",
                                                "light",
                                                "gradient",
                                                "minimal"
                                            ]
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not enough data for this period",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    }
                },
                "operationId": "getWrappedByPeriod",
                "security": [
                    {
                        "bearerAuth": []
                    }
                ]
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
                "description": "Short-lived access token (15 min) from `POST /v1/auth/apple` or `POST /v1/auth/refresh`.\n\nThe session behind the token is checked on every request, so a sign-out takes effect immediately rather than when the token expires."
            }
        },
        "parameters": {
            "Locale": {
                "name": "X-Welleva-Locale",
                "in": "header",
                "required": false,
                "schema": {
                    "type": "string",
                    "enum": [
                        "pt-BR",
                        "en",
                        "es"
                    ]
                },
                "description": "Overrides the account locale for this request. Falls back to `Accept-Language`, then `pt-BR`."
            },
            "AppVersion": {
                "name": "X-Welleva-App-Version",
                "in": "header",
                "required": false,
                "schema": {
                    "type": "string",
                    "example": "1.4.2"
                },
                "description": "Client build. Drives remote-config segmentation and analytics."
            },
            "RequestId": {
                "name": "X-Request-Id",
                "in": "header",
                "required": false,
                "schema": {
                    "type": "string",
                    "maxLength": 40
                },
                "description": "Correlation id. Echoed back on the response and present in every log line for the request."
            },
            "IdempotencyKey": {
                "name": "X-Idempotency-Key",
                "in": "header",
                "required": false,
                "schema": {
                    "type": "string",
                    "maxLength": 128
                },
                "description": "Makes a retry safe. Replaying a key returns the stored response instead of repeating the work — which is what stops a dropped response from billing a second AI analysis.\n\nReusing a key with a *different* body is a client bug and returns `409 idempotency_key_reused`."
            },
            "Limit": {
                "name": "limit",
                "in": "query",
                "required": false,
                "schema": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 25
                }
            },
            "Cursor": {
                "name": "cursor",
                "in": "query",
                "required": false,
                "schema": {
                    "type": "string"
                },
                "description": "Opaque cursor from `meta.next_cursor` of the previous page."
            }
        },
        "headers": {
            "X-Request-Id": {
                "description": "Correlation id for this request. Quote it in any support enquiry.",
                "schema": {
                    "type": "string"
                }
            },
            "X-RateLimit-Remaining": {
                "description": "Requests left in the current window for this bucket.",
                "schema": {
                    "type": "integer"
                }
            },
            "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                    "type": "integer"
                }
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "error"
                ],
                "properties": {
                    "error": {
                        "type": "object",
                        "required": [
                            "code",
                            "message"
                        ],
                        "properties": {
                            "code": {
                                "type": "string",
                                "description": "Stable machine-readable code. Branch on this, never on the message.",
                                "example": "validation_failed"
                            },
                            "message": {
                                "type": "string",
                                "description": "Human-readable explanation, safe to show to the user."
                            },
                            "details": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "Context: offending fields, limits, paywall hints."
                            },
                            "request_id": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "ValidationError": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Error"
                    },
                    {
                        "type": "object",
                        "example": {
                            "error": {
                                "code": "validation_failed",
                                "message": "The given data was invalid.",
                                "details": {
                                    "fields": {
                                        "height_cm": [
                                            "The field height_cm must be between 120, 230."
                                        ]
                                    }
                                }
                            }
                        }
                    }
                ]
            },
            "Tokens": {
                "type": "object",
                "required": [
                    "access_token",
                    "refresh_token",
                    "token_type",
                    "expires_in"
                ],
                "properties": {
                    "access_token": {
                        "type": "string",
                        "description": "Bearer JWT, valid for `expires_in` seconds."
                    },
                    "refresh_token": {
                        "type": "string",
                        "description": "Opaque, single-use. Rotated on every refresh; presenting a rotated token revokes the whole session family as a theft signal."
                    },
                    "token_type": {
                        "type": "string",
                        "enum": [
                            "Bearer"
                        ]
                    },
                    "expires_in": {
                        "type": "integer",
                        "example": 900
                    }
                }
            },
            "User": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "Opaque public id. Never an incrementing integer.",
                        "example": "usr_m8k2p4nq7xw3z9a"
                    },
                    "username": {
                        "type": "string",
                        "nullable": true,
                        "example": "ana.silva"
                    },
                    "display_name": {
                        "type": "string",
                        "nullable": true
                    },
                    "avatar_url": {
                        "type": "string",
                        "nullable": true
                    },
                    "email": {
                        "type": "string",
                        "nullable": true,
                        "description": "Only present on your own profile. Apple sends it once, at first authorisation."
                    },
                    "locale": {
                        "type": "string",
                        "enum": [
                            "pt-BR",
                            "en",
                            "es"
                        ]
                    },
                    "timezone": {
                        "type": "string",
                        "example": "America/Sao_Paulo"
                    },
                    "unit_system": {
                        "type": "string",
                        "enum": [
                            "metric",
                            "imperial"
                        ]
                    },
                    "birth_date": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "sex": {
                        "type": "string",
                        "enum": [
                            "female",
                            "male",
                            "other",
                            "undisclosed"
                        ]
                    },
                    "height_cm": {
                        "type": "number",
                        "nullable": true
                    },
                    "member_since": {
                        "type": "string",
                        "format": "date"
                    }
                }
            },
            "Entitlement": {
                "type": "object",
                "description": "What the server will actually allow. StoreKit decides what the app *shows*; this decides what the API *does*.",
                "properties": {
                    "plan": {
                        "type": "string",
                        "enum": [
                            "free",
                            "premium",
                            "lifetime"
                        ]
                    },
                    "plan_label": {
                        "type": "string",
                        "example": "Welleva+"
                    },
                    "active": {
                        "type": "boolean"
                    },
                    "is_trial": {
                        "type": "boolean"
                    },
                    "expires_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "source": {
                        "type": "string",
                        "enum": [
                            "none",
                            "app_store",
                            "promo",
                            "admin",
                            "trial"
                        ]
                    },
                    "features": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "boolean"
                        }
                    },
                    "limits": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "integer"
                        },
                        "description": "`-1` means unlimited."
                    }
                }
            },
            "Goal": {
                "type": "object",
                "properties": {
                    "metric": {
                        "type": "string",
                        "enum": [
                            "calories",
                            "protein_g",
                            "carbs_g",
                            "fat_g",
                            "fiber_g",
                            "water_ml",
                            "steps",
                            "sleep_minutes",
                            "active_minutes",
                            "mindful_minutes",
                            "weight_kg",
                            "fasting_minutes"
                        ]
                    },
                    "target": {
                        "type": "number"
                    },
                    "unit": {
                        "type": "string"
                    },
                    "source": {
                        "type": "string",
                        "enum": [
                            "onboarding",
                            "adaptive",
                            "custom"
                        ]
                    },
                    "rationale": {
                        "type": "string",
                        "nullable": true,
                        "description": "Why this number. Shown in-app so a target never looks arbitrary."
                    },
                    "effective_from": {
                        "type": "string",
                        "format": "date"
                    }
                }
            },
            "FoodItem": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "brand": {
                        "type": "string",
                        "nullable": true
                    },
                    "category": {
                        "type": "string",
                        "nullable": true
                    },
                    "source": {
                        "type": "string",
                        "enum": [
                            "taco",
                            "usda",
                            "off",
                            "user",
                            "ai",
                            "curated"
                        ]
                    },
                    "barcode": {
                        "type": "string",
                        "nullable": true
                    },
                    "verified": {
                        "type": "boolean"
                    },
                    "is_liquid": {
                        "type": "boolean"
                    },
                    "per_100": {
                        "type": "object",
                        "description": "Always per 100 g (or 100 ml when `is_liquid`). This is what makes items from different datasets summable.",
                        "properties": {
                            "unit": {
                                "type": "string",
                                "enum": [
                                    "g",
                                    "ml"
                                ]
                            },
                            "kcal": {
                                "type": "number"
                            },
                            "protein_g": {
                                "type": "number"
                            },
                            "carbs_g": {
                                "type": "number"
                            },
                            "fat_g": {
                                "type": "number"
                            },
                            "fiber_g": {
                                "type": "number",
                                "nullable": true
                            },
                            "sugar_g": {
                                "type": "number",
                                "nullable": true
                            },
                            "sodium_mg": {
                                "type": "number",
                                "nullable": true
                            }
                        }
                    },
                    "portions": {
                        "type": "array",
                        "description": "Named portions people actually use (\"1 concha\", \"1 fatia\").",
                        "items": {
                            "type": "object",
                            "properties": {
                                "label": {
                                    "type": "string",
                                    "example": "1 concha"
                                },
                                "grams": {
                                    "type": "number",
                                    "example": 80
                                },
                                "is_default": {
                                    "type": "boolean"
                                }
                            }
                        }
                    }
                }
            },
            "AnalysisItem": {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "grams": {
                        "type": "number"
                    },
                    "kcal": {
                        "type": "number"
                    },
                    "protein_g": {
                        "type": "number"
                    },
                    "carbs_g": {
                        "type": "number"
                    },
                    "fat_g": {
                        "type": "number"
                    },
                    "fiber_g": {
                        "type": "number",
                        "nullable": true
                    },
                    "sugar_g": {
                        "type": "number",
                        "nullable": true
                    },
                    "sodium_mg": {
                        "type": "number",
                        "nullable": true
                    },
                    "confidence": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 1,
                        "description": "Per-item confidence. Below ~0.5, prompt the user to confirm."
                    },
                    "portion_description": {
                        "type": "string",
                        "nullable": true,
                        "example": "1 concha"
                    }
                }
            },
            "FoodAnalysis": {
                "type": "object",
                "properties": {
                    "analysis_id": {
                        "type": "string"
                    },
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/AnalysisItem"
                        }
                    },
                    "totals": {
                        "type": "object",
                        "properties": {
                            "kcal": {
                                "type": "number"
                            },
                            "protein_g": {
                                "type": "number"
                            },
                            "carbs_g": {
                                "type": "number"
                            },
                            "fat_g": {
                                "type": "number"
                            },
                            "fiber_g": {
                                "type": "number"
                            },
                            "grams": {
                                "type": "number"
                            }
                        }
                    },
                    "confidence": {
                        "type": "number"
                    },
                    "meal_type": {
                        "type": "string",
                        "nullable": true,
                        "enum": [
                            "breakfast",
                            "lunch",
                            "dinner",
                            "snack",
                            null
                        ]
                    },
                    "note": {
                        "type": "string",
                        "nullable": true,
                        "description": "Set when the image was not food, or was too unclear. `items` is then empty."
                    },
                    "cached": {
                        "type": "boolean",
                        "description": "True when the identical photo/description was analysed before. Costs nothing."
                    },
                    "model": {
                        "type": "string"
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "DailyAggregate": {
                "type": "object",
                "description": "One day of totals. **Daily aggregates only** — raw HealthKit samples never leave the device.",
                "properties": {
                    "day": {
                        "type": "string",
                        "format": "date"
                    },
                    "steps": {
                        "type": "integer",
                        "nullable": true
                    },
                    "active_kcal": {
                        "type": "integer",
                        "nullable": true
                    },
                    "resting_kcal": {
                        "type": "integer",
                        "nullable": true
                    },
                    "distance_m": {
                        "type": "integer",
                        "nullable": true
                    },
                    "exercise_minutes": {
                        "type": "integer",
                        "nullable": true
                    },
                    "workouts_count": {
                        "type": "integer",
                        "nullable": true
                    },
                    "sleep_minutes": {
                        "type": "integer",
                        "nullable": true
                    },
                    "sleep_deep_min": {
                        "type": "integer",
                        "nullable": true
                    },
                    "sleep_rem_min": {
                        "type": "integer",
                        "nullable": true
                    },
                    "sleep_efficiency": {
                        "type": "number",
                        "nullable": true
                    },
                    "resting_hr": {
                        "type": "integer",
                        "nullable": true
                    },
                    "hrv_ms": {
                        "type": "number",
                        "nullable": true
                    },
                    "vo2_max": {
                        "type": "number",
                        "nullable": true
                    },
                    "weight_kg": {
                        "type": "number",
                        "nullable": true
                    },
                    "body_fat_pct": {
                        "type": "number",
                        "nullable": true
                    },
                    "kcal_in": {
                        "type": "integer",
                        "nullable": true
                    },
                    "protein_g": {
                        "type": "integer",
                        "nullable": true
                    },
                    "water_ml": {
                        "type": "integer",
                        "nullable": true
                    },
                    "meals_logged": {
                        "type": "integer",
                        "nullable": true
                    },
                    "fasting_minutes": {
                        "type": "integer",
                        "nullable": true
                    },
                    "mindful_minutes": {
                        "type": "integer",
                        "nullable": true
                    },
                    "mood_score": {
                        "type": "integer",
                        "nullable": true,
                        "minimum": 1,
                        "maximum": 5
                    },
                    "daily_score": {
                        "type": "integer",
                        "nullable": true,
                        "minimum": 0,
                        "maximum": 100
                    },
                    "sleep_score": {
                        "type": "integer",
                        "nullable": true,
                        "minimum": 0,
                        "maximum": 100
                    }
                }
            },
            "Streak": {
                "type": "object",
                "properties": {
                    "streak_type": {
                        "type": "string",
                        "enum": [
                            "daily_log",
                            "water_goal",
                            "steps_goal",
                            "protein_goal",
                            "fasting",
                            "sleep_goal",
                            "meditation",
                            "weigh_in",
                            "all_goals"
                        ]
                    },
                    "current_length": {
                        "type": "integer"
                    },
                    "longest_length": {
                        "type": "integer"
                    },
                    "last_day": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    }
                }
            },
            "Challenge": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "title": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string",
                        "nullable": true
                    },
                    "metric": {
                        "type": "string",
                        "enum": [
                            "steps",
                            "water_ml",
                            "active_minutes",
                            "fasting_minutes",
                            "sleep_minutes",
                            "protein_g",
                            "mindful_minutes",
                            "daily_goal_hits",
                            "workouts"
                        ]
                    },
                    "mode": {
                        "type": "string",
                        "enum": [
                            "total",
                            "average",
                            "streak",
                            "goal_hits"
                        ]
                    },
                    "target": {
                        "type": "number",
                        "nullable": true
                    },
                    "visibility": {
                        "type": "string",
                        "enum": [
                            "private",
                            "friends",
                            "public"
                        ]
                    },
                    "starts_on": {
                        "type": "string",
                        "format": "date"
                    },
                    "ends_on": {
                        "type": "string",
                        "format": "date"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "draft",
                            "upcoming",
                            "active",
                            "completed",
                            "cancelled"
                        ]
                    },
                    "participants": {
                        "type": "integer"
                    },
                    "my_progress": {
                        "type": "number",
                        "nullable": true
                    },
                    "my_rank": {
                        "type": "integer",
                        "nullable": true
                    },
                    "join_code": {
                        "type": "string",
                        "nullable": true,
                        "description": "Returned to the owner only."
                    }
                }
            },
            "LeaderboardEntry": {
                "type": "object",
                "properties": {
                    "rank": {
                        "type": "integer"
                    },
                    "user": {
                        "$ref": "#/components/schemas/User"
                    },
                    "value": {
                        "type": "number"
                    },
                    "is_you": {
                        "type": "boolean"
                    }
                }
            },
            "Envelope": {
                "type": "object",
                "description": "Every successful response uses this shape.",
                "properties": {
                    "data": {
                        "description": "The payload: an object or an array."
                    },
                    "meta": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "Pagination, counts, quota state."
                    }
                }
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "Missing, malformed, expired or revoked access token.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "token_expired",
                                "message": "Token has expired."
                            }
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "Authenticated, but not permitted — consent missing, opt-in required, or not the owner.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "PaymentRequired": {
                "description": "Free-tier limit or Premium-only capability. `details.paywall` names the contextual paywall to present.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "premium_required",
                                "message": "You have used all your free AI analyses this week.",
                                "details": {
                                    "paywall": "ai_photo_limit",
                                    "limit": 3,
                                    "period": "week",
                                    "resets_at": "2026-09-01T00:00:00+00:00",
                                    "can_buy_credits": true
                                }
                            }
                        }
                    }
                }
            },
            "NotFound": {
                "description": "No such resource. Also returned when a resource exists but the caller must not learn that it does.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "Conflict": {
                "description": "Conflicts with current state (duplicate, already joined, idempotency key reused).",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "ValidationFailed": {
                "description": "Input failed validation. `details.fields` maps each field to its messages.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/ValidationError"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Rate limit exceeded for this bucket.",
                "headers": {
                    "Retry-After": {
                        "$ref": "#/components/headers/Retry-After"
                    }
                },
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "ServiceUnavailable": {
                "description": "A dependency is down, or a spend guardrail has tripped. Retry after the indicated delay.",
                "headers": {
                    "Retry-After": {
                        "$ref": "#/components/headers/Retry-After"
                    }
                },
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        }
    },
    "security": [
        {
            "bearerAuth": []
        }
    ]
}