{"openapi":"3.1.0","info":{"title":"WorldPop Geospatial API","description":"\nHigh-performance API for WorldPop demographic data analysis.\n\n## Features\n- **Population Analysis** - Calculate total population within polygon area\n- **Age-Sex Demographics** - Get detailed demographic breakdowns by age and sex\n- **Async Processing** - Submit tasks and poll for results\n- **High Resolution** - 100m resolution data\n\n## Data Source\nAccess Our New [Global 2 Population Data](https://hub.worldpop.org/project/categories?id=3) – 2015 to 2030 \n\n## Support\nTo get support, contact:\n- **Support**: info@worldpop.soton.ac.uk\n- **Tech Support**: b.nosatiuk@soton.ac.uk\n- **Website**: [worldpop.org](https://www.worldpop.org)\n","license":{"name":"Creative Commons Attribution 4.0","url":"https://creativecommons.org/licenses/by/4.0/"},"version":"2.0.0"},"servers":[{"url":"/v2"}],"paths":{"/health":{"get":{"tags":["System"],"summary":"Health Check","description":"Basic health check","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthCheckResponse"}}}}}}},"/ready":{"get":{"tags":["System"],"summary":"Readiness Check","description":"Readiness check - verify all dependencies are available","operationId":"readiness_check_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessCheckResponse"}}}}}}},"/":{"get":{"tags":["Legacy Compatibility"],"summary":"Home Page","description":"Legacy home page\nMatches: GET /","operationId":"home_page__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/login":{"post":{"tags":["Authentication"],"summary":"Login","description":"Login and get access token\n\nUse form data with:\n- **username**: Your username\n- **password**: Your password\n\nReturns JWT token for authenticating other requests.","operationId":"login_auth_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_auth_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/register":{"post":{"tags":["Authentication"],"summary":"Register","description":"Public user registration\n\nAnyone can register. New users are created with:\n- is_authorized=False (admin must authorize before API key works)\n- Default rate limits\n\nAfter registration, user must wait for admin authorization\nbefore their API key becomes active.","operationId":"register_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserRegister"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/password-reset/request":{"post":{"tags":["Authentication"],"summary":"Password Reset Request","description":"Request a password reset link\n\nAlways answers with the same message, whether or not the address belongs to\nan account. Telling the two apart would let anyone test which addresses are\nregistered.","operationId":"password_reset_request_auth_password_reset_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/password-reset/confirm":{"post":{"tags":["Authentication"],"summary":"Password Reset Confirm","description":"Set a new password using a token from the reset email\n\nThe token is single-use and expires. Completing a reset signs out any\nsession that was already open on the account.","operationId":"password_reset_confirm_auth_password_reset_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/me":{"get":{"tags":["Authentication"],"summary":"Get Current User Info","description":"Get current user dashboard with usage statistics\n\nRequires authentication.\nReturns user info, authorization status, and today's usage.\nAPI key is only shown if user is authorized.","operationId":"get_current_user_info_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDashboard"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"delete":{"tags":["Authentication"],"summary":"Delete Own Account","description":"Delete own account\n\nUsers can delete their own account. This action is irreversible.\nAdmin accounts cannot be deleted.","operationId":"delete_own_account_auth_me_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/auth/users":{"get":{"tags":["User Management"],"summary":"List Users","description":"List all users (admin only)\n\nRequires admin authentication.\nReturns all user info including API keys and limits.","operationId":"list_users_auth_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserResponse"},"type":"array","title":"Response List Users Auth Users Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/auth/users/{user_id}":{"put":{"tags":["User Management"],"summary":"Update User","description":"Update user settings (admin only)\n\nCan update email, daily_request_limit, max_area_km2, is_authorized.","operationId":"update_user_auth_users__user_id__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["User Management"],"summary":"Delete User","description":"Delete a user (admin only)","operationId":"delete_user_auth_users__user_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/users/usage/all":{"get":{"tags":["User Management"],"summary":"Get All Users Usage","description":"Get usage statistics for all users (admin only)\n\nReturns today's request count and remaining quota for all users in a single request.","operationId":"get_all_users_usage_auth_users_usage_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserUsageResponse"},"type":"array","title":"Response Get All Users Usage Auth Users Usage All Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/auth/users/{user_id}/usage":{"get":{"tags":["User Management"],"summary":"Get User Usage","description":"Get user's current usage statistics (admin only)\n\nReturns today's request count and remaining quota.","operationId":"get_user_usage_auth_users__user_id__usage_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/users/usage/history":{"get":{"tags":["User Management"],"summary":"Get Users Usage History","description":"Get usage history for top users by day (admin only)\n\nReturns daily request counts for the top N users over the specified period.\nUsed for the usage timeline chart.","operationId":"get_users_usage_history_auth_users_usage_history_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserUsageHistory"},"title":"Response Get Users Usage History Auth Users Usage History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/population":{"post":{"tags":["Population Analysis"],"summary":"Calculate Population","description":"Calculate total population within a polygon area\n\nThis endpoint submits an asynchronous task to calculate the total population\nwithin the specified GeoJSON polygon using WorldPop demographic data.\n\n**Process:**\n1. Submit request with GeoJSON polygon and year\n2. Receive task_id in response\n3. Poll task status using `/api/v1/tasks/{task_id}`\n4. Retrieve result when status is 'SUCCESS'\n\n**Parameters:**\n- **geojson**: Polygon or MultiPolygon geometry in WGS84 (EPSG:4326)\n- **year**: Data year (2015-2030, based on WorldPop availability)\n- **calculation_limit_km2**: Optional area limit override\n- **resolution**: Data resolution 100m\n\n**Returns:**\n- **task_id**: Use this to check task status\n- **check_url**: Direct URL to poll for results\n\n**Example Result (when task completes via /tasks/{task_id}):**\n```json\n{\n    \"task_id\": \"...\",\n    \"status\": \"success\",\n    \"progress\": 100.0,\n    \"stage\": \"Completed\",\n    \"result\": {\n        \"total_population\": 125432.67,\n        \"area_km2\": 15.25,\n        \"data_year\": 2020,\n        \"data_source\": \"WorldPop Global 2 Population Data\",\n        \"processing_time_ms\": 1850,\n        \"population_density\": 8223.45\n    },\n    \"error\": null\n}\n```","operationId":"calculate_population_population_post","parameters":[{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PopulationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSubmissionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/agesex":{"post":{"tags":["Population Analysis"],"summary":"Calculate Agesex","description":"Calculate age-sex demographic breakdown within a polygon area\n\nThis endpoint submits an asynchronous task to analyze the demographic structure\n(age and sex distribution) within the specified GeoJSON polygon.\n\n**Process:**\n1. Submit request with GeoJSON polygon, year, and demographic filters\n2. Receive task_id in response\n3. Poll task status using `/api/v1/tasks/{task_id}`\n4. Retrieve detailed demographic breakdown when complete\n\n**Parameters:**\n- **geojson**: Polygon or MultiPolygon geometry in WGS84 (EPSG:4326)\n- **year**: Data year (2015-2030)\n- **age_range**: Optional age filter [min, max] (e.g., [0, 20] for children)\n- **sex**: Filter by sex: 'male', 'female', or 'both'\n- **resolution**: Data resolution (100m or 1km)\n\n**Returns:**\n- **task_id**: Use this to check task status\n- **check_url**: Direct URL to poll for results\n\n**Example Result (when task completes via /tasks/{task_id}):**\n```json\n{\n    \"task_id\": \"...\",\n    \"status\": \"success\",\n    \"progress\": 100.0,\n    \"stage\": \"Completed\",\n    \"result\": {\n        \"agesex_pyramid\": [\n            {\"class\": \"00\", \"age\": \"0 to 1\", \"female\": 1250.5, \"male\": 1312.3},\n            {\"class\": \"01\", \"age\": \"1 to 5\", \"female\": 5420.1, \"male\": 5678.9}\n        ],\n        \"total_population\": 125432.67,\n        \"area_km2\": 15.25,\n        \"data_year\": 2020,\n        \"data_source\": \"WorldPop Global 2 Population Data\",\n        \"age_range\": [0, 90],\n        \"sex\": \"both\",\n        \"processing_time_ms\": 2500\n    },\n    \"error\": null\n}\n```","operationId":"calculate_agesex_agesex_post","parameters":[{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgeSexRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSubmissionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tasks/{task_id}":{"get":{"tags":["Tasks"],"summary":"Get Task Status","description":"Get status and result of a specific task\n\nPoll this endpoint to check the progress and retrieve results of submitted tasks.\n\n**Task Statuses:**\n- **pending**: Task is queued, waiting to be processed\n- **received**: Task has been received by a worker\n- **started**: Task processing has begun\n- **progress**: Task is in progress (check progress field)\n- **success**: Task completed successfully (result available)\n- **failure**: Task failed (error message available)\n- **retry**: Task is being retried after failure\n\n**Polling Strategy:**\n- Poll every 1-2 seconds for small areas\n- Poll every 3-5 seconds for large areas\n- Stop polling when status is SUCCESS or FAILURE\n\n**Parameters:**\n- **task_id**: Unique task identifier returned when submitting the task\n\n**Returns:**\n- **task_id**: The requested task ID\n- **status**: Current task status\n- **progress**: Progress percentage (0-100) if available\n- **stage**: Human-readable description of current stage\n- **result**: Full result data (only for SUCCESS status)\n- **error**: Error message (only for FAILURE status)\n\n**Example - Task in Progress:**\n```json\n{\n    \"task_id\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"status\": \"progress\",\n    \"progress\": 65.5,\n    \"stage\": \"Calculating population via VRT\",\n    \"result\": null,\n    \"error\": null\n}\n```\n\n**Example - Task Completed:**\n```json\n{\n    \"task_id\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"status\": \"success\",\n    \"progress\": 100.0,\n    \"stage\": \"Completed\",\n    \"result\": {\n        \"total_population\": 125432.67,\n        \"area_km2\": 15.25,\n        \"data_year\": 2020,\n        \"data_source\": \"WorldPop Global 2 Population Data\",\n        \"processing_time_ms\": 1850,\n        \"population_density\": 8223.45\n    },\n    \"error\": null\n}\n```\n\n**Example - Task Failed:**\n```json\n{\n    \"task_id\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"status\": \"failure\",\n    \"progress\": null,\n    \"stage\": null,\n    \"result\": null,\n    \"error\": \"Invalid geometry: Self-intersecting polygon\"\n}\n```","operationId":"get_task_status_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Tasks"],"summary":"Cancel Task","description":"Cancel a running or pending task\n\nAttempts to revoke/cancel a task. Note that cancellation may not be immediate\nfor tasks that are already processing.\n\n**Parameters:**\n- **task_id**: Unique task identifier\n\n**Returns:**\n- Success message if cancellation was requested\n- Error if task cannot be cancelled\n\n**Note:**\n- Tasks in PENDING or RECEIVED state can be cancelled immediately\n- Tasks in PROGRESS may continue until the next checkpoint\n- Completed (SUCCESS/FAILURE) tasks cannot be cancelled\n\n**Example Response:**\n```json\n{\n    \"message\": \"Task cancellation requested\",\n    \"task_id\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"status\": \"revoked\"\n}\n```","operationId":"cancel_task_tasks__task_id__delete","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tasks/{task_id}/result":{"get":{"tags":["Tasks"],"summary":"Get Task Result","description":"Get only the result of a completed task (without metadata)\n\nThis is a convenience endpoint that returns just the result payload\nwithout task state information.\n\n**Parameters:**\n- **task_id**: Unique task identifier\n\n**Returns:**\n- Task result data (if task is completed)\n- Error if task is not yet completed or failed\n\n**Example Response:**\n```json\n{\n    \"total_population\": 125432.67,\n    \"area_km2\": 15.25,\n    \"data_year\": 2020,\n    \"data_source\": \"WorldPop Global 2 Population Data\",\n    \"processing_time_ms\": 1850,\n    \"population_density\": 8223.45\n}\n```","operationId":"get_task_result_tasks__task_id__result_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/wp_popsum/submit":{"post":{"tags":["Legacy Compatibility"],"summary":"Wp Popsum Submit","description":"Submit population sum calculation (legacy format)\n\n**Legacy Endpoint** - For backward compatibility with old API\n\nMatches: POST /wp_popsum/submit\n\n**Form Parameters:**\n- **geojson_text**: JSON string containing wpRequest and geometry\n\n**Example geojson_text:**\n```json\n{\n    \"wpRequest\": \"population_sum\",\n    \"type\": \"Polygon\",\n    \"coordinates\": [[...]],\n    \"year\": 2020\n}\n```\n\n**Returns:**\n- Legacy format response with UUID for checking status\n- Use `/check/{uuid}` or `/check/{uuid}.json` to get results","operationId":"wp_popsum_submit_wp_popsum_submit_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_wp_popsum_submit_wp_popsum_submit_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LegacyTaskSubmissionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/wp_agesexsum/submit":{"post":{"tags":["Legacy Compatibility"],"summary":"Wp Agesexsum Submit","description":"Submit age-sex analysis (legacy format)\n\n**Legacy Endpoint** - For backward compatibility with old API\n\nMatches: POST /wp_agesexsum/submit\n\n**Form Parameters:**\n- **geojson_text**: JSON string containing wpRequest and geometry\n\n**Example geojson_text:**\n```json\n{\n    \"wpRequest\": \"agesex_analysis\",\n    \"type\": \"Polygon\",\n    \"coordinates\": [[...]],\n    \"year\": 2020,\n    \"age_range\": [0, 20],\n    \"sex\": \"both\"\n}\n```\n\n**Returns:**\n- Legacy format response with UUID for checking status\n- Use `/check/{uuid}` or `/check/{uuid}.json` to get results","operationId":"wp_agesexsum_submit_wp_agesexsum_submit_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_wp_agesexsum_submit_wp_agesexsum_submit_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LegacyTaskSubmissionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/check":{"get":{"tags":["Legacy Compatibility"],"summary":"List Tasks","description":"List all tasks with status (legacy format)\n\n**Legacy Endpoint** - Returns HTML page with task list\n\nMatches: GET /check","operationId":"list_tasks_check_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/check/{task_uuid}":{"get":{"tags":["Legacy Compatibility"],"summary":"Check Task Status","description":"Check specific task status (legacy format)\n\n**Legacy Endpoint** - Matches original v1 API format exactly\n\nSupports both HTML and JSON responses:\n- `/check/{uuid}` - Returns HTML page\n- `/check/{uuid}.json` - Returns JSON response\n\n**Parameters:**\n- **task_uuid**: Task identifier (with optional .json extension)\n\n**Example JSON Response (finished):**\n```json\n{\n    \"status\": \"finished\",\n    \"status_code\": 200,\n    \"error\": false,\n    \"error_message\": null,\n    \"data\": {\n        \"total_population\": 1705754.5\n    },\n    \"taskid\": \"ee2e98e9-b9ea-5eb6-9c26-ecb8cb3469a3\",\n    \"startTime\": \"2019-10-16 10:26:03\",\n    \"endTime\": \"2019-10-16 10:26:04\",\n    \"executionTime\": 1\n}\n```","operationId":"check_task_status_check__task_uuid__get","parameters":[{"name":"task_uuid","in":"path","required":true,"schema":{"type":"string","title":"Task Uuid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"APIInfoResponse":{"properties":{"name":{"type":"string","title":"Name","description":"API name"},"version":{"type":"string","title":"Version","description":"API version"},"description":{"type":"string","title":"Description","description":"API description"},"endpoints":{"items":{"type":"string"},"type":"array","title":"Endpoints","description":"Available endpoints"},"supported_years":{"items":{"type":"integer"},"type":"array","title":"Supported Years","description":"Supported data years"},"max_area_km2":{"type":"number","title":"Max Area Km2","description":"Maximum calculation area in km²"},"documentation_url":{"type":"string","title":"Documentation Url","description":"API documentation URL"},"contact":{"additionalProperties":{"type":"string"},"type":"object","title":"Contact","description":"Contact information","example":{"email":"info@worldpop.soton.ac.uk","website":"https://www.worldpop.org"}}},"type":"object","required":["name","version","description","endpoints","supported_years","max_area_km2","documentation_url","contact"],"title":"APIInfoResponse","description":"API information response"},"AgeSexRequest":{"properties":{"geojson":{"type":"object","title":"Geojson","description":"GeoJSON geometry (Polygon or MultiPolygon)"},"year":{"type":"integer","enum":[2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030],"title":"Year","description":"WorldPop data year (2015-2030)"},"calculation_limit_km2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Calculation Limit Km2","description":"Maximum area in km² (defaults to system limit)"},"age_range":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Age Range","description":"Age range [min, max], defaults to [0, 90]"},"sex":{"type":"string","enum":["male","female","both"],"title":"Sex","description":"Sex filter: 'male', 'female', or 'both'","default":"both"},"resolution":{"type":"string","enum":["100m","1km"],"title":"Resolution","description":"Data resolution: 100m or 1km","default":"100m"}},"type":"object","required":["geojson","year"],"title":"AgeSexRequest","description":"Age-sex demographic analysis request","example":{"age_range":[0,20],"geojson":{"coordinates":[[[-0.15,51.5],[-0.1,51.5],[-0.1,51.48],[-0.15,51.48],[-0.15,51.5]]],"type":"Polygon"},"resolution":"100m","sex":"both","year":2020}},"Body_login_auth_login_post":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"password"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_login_auth_login_post"},"Body_wp_agesexsum_submit_wp_agesexsum_submit_post":{"properties":{"geojson_text":{"type":"string","title":"Geojson Text"}},"type":"object","required":["geojson_text"],"title":"Body_wp_agesexsum_submit_wp_agesexsum_submit_post"},"Body_wp_popsum_submit_wp_popsum_submit_post":{"properties":{"geojson_text":{"type":"string","title":"Geojson Text"}},"type":"object","required":["geojson_text"],"title":"Body_wp_popsum_submit_wp_popsum_submit_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthCheckResponse":{"properties":{"status":{"type":"string","enum":["healthy","unhealthy"],"title":"Status","description":"Service health status"},"version":{"type":"string","title":"Version","description":"API version"},"timestamp":{"type":"number","title":"Timestamp","description":"Check timestamp (Unix timestamp)"},"uptime_seconds":{"type":"number","title":"Uptime Seconds","description":"Service uptime in seconds"}},"type":"object","required":["status","version","timestamp","uptime_seconds"],"title":"HealthCheckResponse","description":"Health check response"},"LegacyTaskSubmissionResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Task status","default":"created"},"status_code":{"type":"integer","title":"Status Code","description":"HTTP status code","default":200},"error":{"type":"boolean","title":"Error","description":"Error flag","default":false},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message","description":"Error message if any"},"taskid":{"type":"string","title":"Taskid","description":"Task UUID"}},"type":"object","required":["taskid"],"title":"LegacyTaskSubmissionResponse","description":"Legacy response format for task submission - matches original v1 API"},"MessageResponse":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"MessageResponse","description":"Generic acknowledgement"},"PasswordResetConfirm":{"properties":{"token":{"type":"string","title":"Token","description":"Token from the emailed link"},"new_password":{"type":"string","minLength":6,"title":"New Password"}},"type":"object","required":["token","new_password"],"title":"PasswordResetConfirm","description":"Finish a password reset"},"PasswordResetRequest":{"properties":{"email":{"type":"string","title":"Email","description":"Email address of the account"}},"type":"object","required":["email"],"title":"PasswordResetRequest","description":"Start a password reset"},"PopulationRequest":{"properties":{"geojson":{"type":"object","title":"Geojson","description":"GeoJSON geometry (Polygon or MultiPolygon)"},"year":{"type":"integer","enum":[2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030],"title":"Year","description":"WorldPop data year (2015-2030)"},"calculation_limit_km2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Calculation Limit Km2","description":"Maximum area in km² (defaults to system limit)"},"resolution":{"type":"string","enum":["100m","1km"],"title":"Resolution","description":"Data resolution: 100m or 1km","default":"100m"}},"type":"object","required":["geojson","year"],"title":"PopulationRequest","description":"Population calculation request","example":{"geojson":{"coordinates":[[[-0.15,51.5],[-0.1,51.5],[-0.1,51.48],[-0.15,51.48],[-0.15,51.5]]],"type":"Polygon"},"resolution":"100m","year":2020}},"ReadinessCheckResponse":{"properties":{"status":{"type":"string","enum":["ready","not_ready"],"title":"Status","description":"Service readiness status"},"checks":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Checks","description":"Individual service checks","example":{"data_files":true,"database":true,"rabbitmq":true,"redis":true}},"timestamp":{"type":"number","title":"Timestamp","description":"Check timestamp (Unix timestamp)"}},"type":"object","required":["status","checks","timestamp"],"title":"ReadinessCheckResponse","description":"Readiness check response"},"TaskStatusResponse":{"properties":{"task_id":{"type":"string","title":"Task Id","description":"Task identifier"},"status":{"type":"string","title":"Status","description":"Current task status"},"progress":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Progress","description":"Progress percentage (0-100)"},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage","description":"Current processing stage"},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result","description":"Task result (if completed)"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message (if failed)"}},"type":"object","required":["task_id","status"],"title":"TaskStatusResponse","description":"Task status response","example":{"progress":100.0,"result":{"area_km2":15.25,"data_source":"WorldPop Global 2 Population Data","data_year":2020,"population_density":8223.45,"processing_time_ms":1850,"total_population":125432.67},"stage":"Completed","status":"success","task_id":"123e4567-e89b-12d3-a456-426614174000"}},"TaskSubmissionResponse":{"properties":{"task_id":{"type":"string","title":"Task Id","description":"Unique task identifier"},"status":{"type":"string","title":"Status","description":"Initial task status"},"message":{"type":"string","title":"Message","description":"Human-readable message"},"check_url":{"type":"string","title":"Check Url","description":"URL to check task status"}},"type":"object","required":["task_id","status","message","check_url"],"title":"TaskSubmissionResponse","description":"Response when task is submitted","example":{"check_url":"/api/v1/tasks/123e4567-e89b-12d3-a456-426614174000","message":"Task submitted successfully","status":"pending","task_id":"123e4567-e89b-12d3-a456-426614174000"}},"Token":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"},"expires_in":{"type":"integer","title":"Expires In","description":"Token expiration in seconds"},"is_admin":{"type":"boolean","title":"Is Admin","description":"Whether user is admin","default":false}},"type":"object","required":["access_token","expires_in"],"title":"Token","description":"Token response"},"UserDailyUsage":{"properties":{"date":{"type":"string","title":"Date"},"requests":{"type":"integer","title":"Requests"}},"type":"object","required":["date","requests"],"title":"UserDailyUsage","description":"Daily usage data for a user"},"UserDashboard":{"properties":{"id":{"type":"integer","title":"Id"},"username":{"type":"string","title":"Username"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"is_authorized":{"type":"boolean","title":"Is Authorized"},"is_admin":{"type":"boolean","title":"Is Admin","default":false},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key"},"daily_request_limit":{"type":"integer","title":"Daily Request Limit"},"max_area_km2":{"type":"number","title":"Max Area Km2"},"requests_today":{"type":"integer","title":"Requests Today"},"requests_remaining":{"type":"integer","title":"Requests Remaining"},"usage_percent":{"type":"number","title":"Usage Percent"},"reset_at":{"type":"string","title":"Reset At"}},"type":"object","required":["id","username","email","is_authorized","daily_request_limit","max_area_km2","requests_today","requests_remaining","usage_percent","reset_at"],"title":"UserDashboard","description":"User dashboard response with usage stats"},"UserRegister":{"properties":{"username":{"type":"string","maxLength":50,"minLength":3,"title":"Username"},"password":{"type":"string","minLength":6,"title":"Password"},"email":{"type":"string","maxLength":100,"title":"Email","description":"Email address"},"company":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Company","description":"Company/organization"},"website":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Website","description":"Website URL"}},"type":"object","required":["username","password","email"],"title":"UserRegister","description":"Public user registration request"},"UserResponse":{"properties":{"id":{"type":"integer","title":"Id"},"username":{"type":"string","title":"Username"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"is_authorized":{"type":"boolean","title":"Is Authorized"},"is_admin":{"type":"boolean","title":"Is Admin"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key"},"daily_request_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Request Limit"},"max_area_km2":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Area Km2"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","username","email","is_authorized","is_admin"],"title":"UserResponse","description":"User info response"},"UserUpdate":{"properties":{"daily_request_limit":{"anyOf":[{"type":"integer","maximum":10000000.0,"minimum":100.0},{"type":"null"}],"title":"Daily Request Limit"},"max_area_km2":{"anyOf":[{"type":"number","maximum":10000000.0,"minimum":1000.0},{"type":"null"}],"title":"Max Area Km2"},"is_authorized":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Authorized"}},"type":"object","title":"UserUpdate","description":"User update request (admin only)\n\nDeliberately no email field. Changing the address on someone's account\nredirects their password reset and API key mail, which turns admin access\ninto account takeover; and an admin has no legitimate reason to edit it.\nA genuinely wrong address is fixed by deleting the account and registering\nagain."},"UserUsageHistory":{"properties":{"user_id":{"type":"integer","title":"User Id"},"username":{"type":"string","title":"Username"},"api_key_prefix":{"type":"string","title":"Api Key Prefix"},"daily_usage":{"items":{"$ref":"#/components/schemas/UserDailyUsage"},"type":"array","title":"Daily Usage"},"total_requests":{"type":"integer","title":"Total Requests"}},"type":"object","required":["user_id","username","api_key_prefix","daily_usage","total_requests"],"title":"UserUsageHistory","description":"User usage history for chart"},"UserUsageResponse":{"properties":{"user_id":{"type":"integer","title":"User Id"},"username":{"type":"string","title":"Username"},"daily_request_limit":{"type":"integer","title":"Daily Request Limit"},"requests_today":{"type":"integer","title":"Requests Today"},"requests_remaining":{"type":"integer","title":"Requests Remaining"},"usage_percent":{"type":"number","title":"Usage Percent"},"reset_at":{"type":"string","title":"Reset At"}},"type":"object","required":["user_id","username","daily_request_limit","requests_today","requests_remaining","usage_percent","reset_at"],"title":"UserUsageResponse","description":"User usage statistics"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/v2/auth/login"}}}}},"tags":[{"name":"System","description":"Health checks and system information"},{"name":"Population Analysis","description":"Main endpoints for population calculations using WorldPop data"},{"name":"Tasks","description":"Check status and retrieve results of submitted tasks"},{"name":"Authentication","description":"User registration, login, and account management"},{"name":"User Management","description":"Admin endpoints for managing users and their limits"},{"name":"Legacy Compatibility","description":"**Deprecated endpoints** - maintained for backward compatibility with v1 API. Use Population Analysis endpoints instead."}]}