{"openapi":"3.1.0","info":{"title":"Location service","description":"Location ingest, history, and geofence/landed/stale subscriptions. Human docs: [/docs](/docs).","version":"1.0.0"},"paths":{"/healthz":{"get":{"tags":["meta"],"summary":"Healthz","description":"Liveness + a tiny bit of state.\n1. Count fixes and report the newest fix time (no coordinates; endpoint is public).","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/ingest/overland":{"post":{"tags":["ingest"],"summary":"Ingest Overland","description":"Overland batch endpoint.\n1. Parse JSON body {\"locations\": [...]}; 400 if it isn't JSON.\n2. Adapter -> LocationFix list -> processing.ingest (in a thread).\n3. Answer {\"result\": \"ok\"} so Overland dequeues the batch.","operationId":"ingest_overland_ingest_overland_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverlandResponse"}}}}}}},"/ingest/owntracks":{"post":{"tags":["ingest"],"summary":"Ingest Owntracks","description":"OwnTracks HTTP-mode endpoint.\n1. Parse JSON; device defaults to the X-Limit-D header if set.\n2. Adapter -> processing.ingest.\n3. Answer [] (OwnTracks expects a JSON array of commands/cards).","operationId":"ingest_owntracks_ingest_owntracks_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{},"type":"array","title":"Response Ingest Owntracks Ingest Owntracks Post"}}}}}}},"/location/current":{"get":{"tags":["location"],"summary":"Location Current","description":"Newest fix.\n1. Optionally for one device; 404 if there are no fixes.","operationId":"location_current_location_current_get","parameters":[{"name":"device","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoredFix"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/location/history":{"get":{"tags":["location"],"summary":"Location History","description":"Fixes in a time range, newest first.\n1. since/until are ISO 8601 (naive = UTC); limit 1..10000 (default 100).","operationId":"location_history_location_history_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":100,"title":"Limit"}},{"name":"device","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixHistory"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["location"],"summary":"Location Delete","description":"Delete one device's fixes (optionally within a time range).\n1. device is required; returns {\"deleted\": n}.","operationId":"location_delete_location_history_delete","parameters":[{"name":"device","in":"query","required":true,"schema":{"type":"string","title":"Device"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Until"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Location Delete Location History Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions":{"post":{"tags":["subscriptions"],"summary":"Subscription Create","description":"Create a subscription.\n1. target=openclaw requires the server to have an OpenClaw hook token configured.\n2. Insert (geofence state seeded from the latest fix) and return it (secret not echoed).","operationId":"subscription_create_subscriptions_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["subscriptions"],"summary":"Subscription List","description":"List subscriptions.\n1. Active only unless include_inactive=true.","operationId":"subscription_list_subscriptions_get","parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Subscription"},"title":"Response Subscription List Subscriptions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/{sub_id}":{"get":{"tags":["subscriptions"],"summary":"Subscription Get","description":"One subscription.\n1. 404 if missing; strip secret/state.","operationId":"subscription_get_subscriptions__sub_id__get","parameters":[{"name":"sub_id","in":"path","required":true,"schema":{"type":"string","title":"Sub Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["subscriptions"],"summary":"Subscription Delete","description":"Delete a subscription (and its undelivered events).\n1. 404 if missing.","operationId":"subscription_delete_subscriptions__sub_id__delete","parameters":[{"name":"sub_id","in":"path","required":true,"schema":{"type":"string","title":"Sub Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events":{"get":{"tags":["subscriptions"],"summary":"Events List","description":"Recent fired events with delivery status, newest first.\n1. Optionally for one subscription.","operationId":"events_list_events_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":50,"title":"Limit"}},{"name":"subscription_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subscription Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EventRecord"},"title":"Response Events List Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"EventPayload":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","enum":["geofence.entered","geofence.exited","landed","stale"],"title":"Type"},"subscription_id":{"type":"string","title":"Subscription Id"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"message":{"type":"string","title":"Message"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"fix":{"anyOf":[{"$ref":"#/components/schemas/LocationFix"},{"type":"null"}]},"previous_fix":{"anyOf":[{"$ref":"#/components/schemas/LocationFix"},{"type":"null"}]},"geofence":{"anyOf":[{"$ref":"#/components/schemas/GeofenceRule"},{"type":"null"}]},"place":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Place"},"distance_km":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Distance Km"},"hours_since_fix":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hours Since Fix"}},"type":"object","required":["id","type","subscription_id","occurred_at","message"],"title":"EventPayload","description":"JSON body POSTed to a callback when a rule fires."},"EventRecord":{"properties":{"id":{"type":"string","title":"Id"},"subscription_id":{"type":"string","title":"Subscription Id"},"type":{"type":"string","enum":["geofence.entered","geofence.exited","landed","stale"],"title":"Type"},"status":{"type":"string","enum":["pending","delivered","failed"],"title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"delivered_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Delivered At"},"payload":{"$ref":"#/components/schemas/EventPayload"}},"type":"object","required":["id","subscription_id","type","status","attempts","last_error","created_at","delivered_at","payload"],"title":"EventRecord","description":"An event plus its delivery status (GET /events)."},"FixHistory":{"properties":{"count":{"type":"integer","title":"Count"},"fixes":{"items":{"$ref":"#/components/schemas/StoredFix"},"type":"array","title":"Fixes"}},"type":"object","required":["count","fixes"],"title":"FixHistory","description":"Response of GET /location/history (newest first)."},"GeofenceRule":{"properties":{"type":{"type":"string","const":"geofence","title":"Type","default":"geofence"},"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat"},"lon":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lon"},"radius_m":{"type":"number","maximum":1000000.0,"exclusiveMinimum":0.0,"title":"Radius M"},"on":{"type":"string","enum":["enter","exit","both"],"title":"On","default":"both"}},"type":"object","required":["name","lat","lon","radius_m"],"title":"GeofenceRule","description":"Fire when the device enters and/or exits a circle."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Health":{"properties":{"ok":{"type":"boolean","title":"Ok"},"fixes":{"type":"integer","title":"Fixes"},"last_fix_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Fix At"}},"type":"object","required":["ok","fixes","last_fix_at"],"title":"Health","description":"GET /healthz."},"LandedRule":{"properties":{"type":{"type":"string","const":"landed","title":"Type","default":"landed"},"min_distance_km":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Distance Km"},"min_gap_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Gap Hours"}},"type":"object","title":"LandedRule","description":"Fire on the first good fix far from the previous one after a gap (i.e. after a flight)."},"LocationFix":{"properties":{"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat"},"lon":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lon"},"accuracy_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Accuracy M"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"battery":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery"},"motion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Motion"},"source":{"type":"string","title":"Source"},"device":{"type":"string","title":"Device"}},"type":"object","required":["lat","lon","timestamp","source","device"],"title":"LocationFix","description":"Normalized fix every source adapter produces. Timestamps are timezone-aware UTC."},"OverlandResponse":{"properties":{"result":{"type":"string","const":"ok","title":"Result","default":"ok"}},"type":"object","title":"OverlandResponse","description":"Overland only dequeues points when it gets exactly {\"result\": \"ok\"}."},"StaleRule":{"properties":{"type":{"type":"string","const":"stale","title":"Type","default":"stale"},"hours":{"type":"number","exclusiveMinimum":0.0,"title":"Hours"}},"type":"object","required":["hours"],"title":"StaleRule","description":"Fire once when no fix has arrived for `hours`."},"StoredFix":{"properties":{"lat":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Lat"},"lon":{"type":"number","maximum":180.0,"minimum":-180.0,"title":"Lon"},"accuracy_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Accuracy M"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"battery":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Battery"},"motion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Motion"},"source":{"type":"string","title":"Source"},"device":{"type":"string","title":"Device"},"id":{"type":"integer","title":"Id"},"received_at":{"type":"string","format":"date-time","title":"Received At"}},"type":"object","required":["lat","lon","timestamp","source","device","id","received_at"],"title":"StoredFix","description":"A fix as stored, with its row id and when the server received it."},"Subscription":{"properties":{"id":{"type":"string","title":"Id"},"rule":{"oneOf":[{"$ref":"#/components/schemas/GeofenceRule"},{"$ref":"#/components/schemas/LandedRule"},{"$ref":"#/components/schemas/StaleRule"}],"title":"Rule","discriminator":{"propertyName":"type","mapping":{"geofence":"#/components/schemas/GeofenceRule","landed":"#/components/schemas/LandedRule","stale":"#/components/schemas/StaleRule"}}},"target":{"type":"string","enum":["webhook","openclaw"],"title":"Target"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"has_secret":{"type":"boolean","title":"Has Secret"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"once":{"type":"boolean","title":"Once"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","rule","target","callback_url","has_secret","note","once","active","created_at"],"title":"Subscription","description":"A subscription as returned by the API (secret is never echoed back)."},"SubscriptionCreate":{"properties":{"rule":{"oneOf":[{"$ref":"#/components/schemas/GeofenceRule"},{"$ref":"#/components/schemas/LandedRule"},{"$ref":"#/components/schemas/StaleRule"}],"title":"Rule","discriminator":{"propertyName":"type","mapping":{"geofence":"#/components/schemas/GeofenceRule","landed":"#/components/schemas/LandedRule","stale":"#/components/schemas/StaleRule"}}},"target":{"type":"string","enum":["webhook","openclaw"],"title":"Target","default":"webhook"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret"},"note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Note"},"once":{"type":"boolean","title":"Once","default":false}},"type":"object","required":["rule"],"title":"SubscriptionCreate","description":"Body of POST /subscriptions."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}