"description":"Return all configuration sections. Secret field values are masked (empty string).",
"operationId":"get_config_api_config_get",
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/ConfigResponse"
}
}
}
}
}
},
"put":{
"tags":[
"api-config"
],
"summary":"Put Config",
"description":"Save configuration updates.\n\n- Blank secret value keeps the existing stored value (no change).\n- Invalid values return 422 and nothing is written to the database.",
"description":"Send a test SMTP email using the current runtime settings.\n\nReturns a structured result indicating success or the category of failure.\nThree possible outcomes:\n- 200 { \"result\": \"success\", \"message\": ... }\n- 400 { \"result\": \"config-error\", \"message\": ... } (EmailConfigurationError)\n- 502 { \"result\": \"failed\", \"message\": ... } (EmailDeliveryError)\n\nSMTP credentials are never echoed in the response.",
"description":"Return location records with optional time-window filtering and pagination.\n\n- ``start`` / ``end`` are ISO8601 strings; filtering is **inclusive** on both bounds.\n- Results are ordered by ``datetime`` ascending.\n- ``limit`` is capped at 5000 to prevent full-table exports.",
"operationId":"get_locations_api_locations_get",
"parameters":[
{
"name":"limit",
"in":"query",
"required":false,
"schema":{
"type":"integer",
"maximum":5000,
"minimum":1,
"default":1000,
"title":"Limit"
}
},
{
"name":"offset",
"in":"query",
"required":false,
"schema":{
"type":"integer",
"minimum":0,
"default":0,
"title":"Offset"
}
},
{
"name":"start",
"in":"query",
"required":false,
"schema":{
"anyOf":[
{
"type":"string"
},
{
"type":"null"
}
],
"title":"Start"
}
},
{
"name":"end",
"in":"query",
"required":false,
"schema":{
"anyOf":[
{
"type":"string"
},
{
"type":"null"
}
],
"title":"End"
}
}
],
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/LocationsResponse"
}
}
}
},
"422":{
"description":"Validation Error",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/poo":{
"get":{
"tags":[
"api-data"
],
"summary":"Get Poo",
"description":"Return poo records ordered by timestamp descending (most recent first).\n\n``limit`` is capped at 1000 to prevent full-table exports.",
"operationId":"get_poo_api_poo_get",
"parameters":[
{
"name":"limit",
"in":"query",
"required":false,
"schema":{
"type":"integer",
"maximum":1000,
"minimum":1,
"default":100,
"title":"Limit"
}
},
{
"name":"offset",
"in":"query",
"required":false,
"schema":{
"type":"integer",
"minimum":0,
"default":0,
"title":"Offset"
}
}
],
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/PooResponse"
}
}
}
},
"422":{
"description":"Validation Error",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/public-ip":{
"get":{
"tags":[
"api-data"
],
"summary":"Get Public Ip",
"description":"Return the current public IP state and recent history.\n\n- ``state`` is ``null`` if no IP check has been performed yet.\n- ``history`` is ordered by ``observed_at`` descending (most recent first).\n- ``limit`` applies to the history list and is capped at 1000.",
"description":"Update the non-PK fields of a single location record.\n\n- ``person`` and ``datetime`` identify the row (composite PK) and are immutable.\n- Only ``latitude``, ``longitude``, and ``altitude`` may be updated.\n- Omitted body fields are left unchanged.\n- Returns **404** if the PK does not exist.",
"description":"Delete the single location record identified by its composite PK.\n\n- Exactly one row is deleted; **404** if the PK does not exist.\n- No batch delete / truncate path is available.",
"description":"Update the non-PK fields of a single poo record.\n\n- ``timestamp`` is the PK and is immutable.\n- Only ``status``, ``latitude``, and ``longitude`` may be updated.\n- Omitted body fields are left unchanged.\n- Returns **404** if the PK does not exist.",
"description":"Delete the single poo record identified by its PK.\n\n- Exactly one row is deleted; **404** if the PK does not exist.\n- No batch delete / truncate path is available.",
"description":"Return the current session user and CSRF token. Returns 401 if not authenticated.",
"operationId":"get_session_api_session_get",
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/SessionResponse"
}
}
}
}
}
}
},
"/api/auth/login":{
"post":{
"tags":[
"api-session"
],
"summary":"Post Login",
"description":"Authenticate with username and password.\n\nOn success, sets an HttpOnly session cookie and returns the session user + CSRF token.\nOn failure, returns 401 with no cookie set.\nNo X-CSRF-Token required (unauthenticated endpoint).",
"operationId":"post_login_api_auth_login_post",
"requestBody":{
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/LoginRequest"
}
}
},
"required":true
},
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/SessionResponse"
}
}
}
},
"422":{
"description":"Validation Error",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/auth/logout":{
"post":{
"tags":[
"api-session"
],
"summary":"Post Logout",
"description":"Revoke the current session and clear the session cookie.\nRequires authentication and X-CSRF-Token header.\nReturns 204 No Content.",
"operationId":"post_logout_api_auth_logout_post",
"parameters":[
{
"name":"X-CSRF-Token",
"in":"header",
"required":false,
"schema":{
"anyOf":[
{
"type":"string"
},
{
"type":"null"
}
],
"title":"X-Csrf-Token"
}
}
],
"responses":{
"200":{
"description":"Successful Response",
"content":{
"application/json":{
"schema":{}
}
}
},
"422":{
"description":"Validation Error",
"content":{
"application/json":{
"schema":{
"$ref":"#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/auth/password":{
"post":{
"tags":[
"api-session"
],
"summary":"Post Change Password",
"description":"Change the current user's password.\nRequires authentication and X-CSRF-Token header.\nOn AuthPasswordChangeError returns 400 with a generic message.\nOn success, force_password_change becomes False (handled by the service).\nReturns 204 No Content.",