{"openapi":"3.1.0","info":{"title":"public-api-mobile-ingest","version":"1.0.63","description":"Mobile GPS firehose. Authenticated POST → Kafka. Per ADR 0006: authenticate, validate, stamp server_received_at, produce to loc_mesh.telemetry.tracking.v1, return 202. No DB writes, no business logic. Source: https://github.com/loc-mesh/public-api-mobile-ingest"},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT (Zitadel-issued, tracking.write scope)","description":"Short-lived Zitadel JWT bound to tenant_id + driver_id + device_id. Verified locally against Zitadel JWKS. In local-dev DEV_BYPASS_AUTH=1 resolves to tenant 1 / driver 1."}},"schemas":{"envelope":{"type":"object","required":["error"],"additionalProperties":false,"properties":{"error":{"type":"object","required":["code","message"],"additionalProperties":false,"properties":{"code":{"type":"string","description":"Stable snake_case code agents key on."},"message":{"type":"string","description":"Human-readable explanation."},"details":{"type":"object","additionalProperties":true,"description":"Optional structured context (e.g. invalid field names)."}}}}}}},"paths":{"/api/openapi.json":{"get":{"summary":"Generated OpenAPI 3.1 contract.","tags":["discovery"],"description":"Authoritative request/response shapes for every endpoint. Generated from per-route schemas — cannot drift from the implementation. Anonymous.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/api":{"get":{"summary":"Self-describing service root.","tags":["discovery"],"description":"Quick-orient overview of the service: purpose, auth scheme, every endpoint, and pointers to the formal OpenAPI doc and Swagger UI. Anonymous.","security":[],"responses":{"200":{"description":"Default Response"}}}},"/api/health":{"get":{"summary":"Liveness probe.","tags":["health"],"description":"Returns 200 if the process is up and serving. Does NOT touch any downstream — Kafka reachability is /api/health/kafka. Anonymous.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok","service","environment"],"additionalProperties":false,"properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"environment":{"type":"string"}}}}}}}}},"/api/health/ready":{"get":{"summary":"Deep readiness probe (ADR 0023).","tags":["health"],"description":"Probes every critical dependency in parallel with a short per-probe timeout and returns the per-dependency result. Overall ok = all critical deps ok; 200 when ready, 503 when not. The body shape is identical on 200 and 503 (deliberate carve-out from the uniform error envelope per ADR 0023). Anonymous.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok","service","environment","dependencies"],"additionalProperties":false,"properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"environment":{"type":"string"},"dependencies":{"type":"array","items":{"type":"object","required":["name","critical","ok","latencyMs"],"additionalProperties":false,"properties":{"name":{"type":"string"},"critical":{"type":"boolean"},"ok":{"type":"boolean"},"latencyMs":{"type":"number"},"error":{"type":"string"}}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok","service","environment","dependencies"],"additionalProperties":false,"properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"environment":{"type":"string"},"dependencies":{"type":"array","items":{"type":"object","required":["name","critical","ok","latencyMs"],"additionalProperties":false,"properties":{"name":{"type":"string"},"critical":{"type":"boolean"},"ok":{"type":"boolean"},"latencyMs":{"type":"number"},"error":{"type":"string"}}}}}}}}}}}},"/api/health/kafka":{"get":{"summary":"Kafka readiness probe.","tags":["health"],"description":"Connects an admin client to the configured Kafka brokers and runs listTopics(). 200 on success, 503 with the uniform error envelope on failure. Anonymous.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok","service","environment","dependency","brokers"],"additionalProperties":false,"properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"environment":{"type":"string"},"dependency":{"type":"string"},"brokers":{"type":"array","items":{"type":"string"}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}}}}},"/api/version":{"get":{"summary":"Service identity + build metadata.","tags":["discovery"],"description":"Standardised body shape across every Mesh API per design/quality-standards.md §3. Includes MESH_ENV so the dev portal and deployment dashboards can show which environment a build is running in. Anonymous.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["service","version","commit","buildTime","environment"],"additionalProperties":false,"properties":{"service":{"type":"string"},"version":{"type":"string"},"commit":{"type":"string"},"buildTime":{"type":"string"},"environment":{"type":"string"}}}}}}}}},"/api/whoami":{"get":{"summary":"Authenticated caller identity (ADR 0023).","tags":["identity"],"description":"Echoes the identity derived from the verified bearer token: subject, token type, tenant, and scopes. Requires a valid Bearer JWT — 401 with the uniform error envelope when absent/invalid. Identity is taken entirely from the verified token, never from the request. tenant.name / tenant.plan are null on this service (no central Postgres connection — it is a write-only Kafka firehose).","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["service","subject","tokenType","tenant","scopes"],"additionalProperties":false,"properties":{"service":{"type":"string"},"subject":{"type":"string"},"tokenType":{"type":"string","enum":["jwt","pat","session"]},"tenant":{"type":"object","required":["id","name","plan"],"additionalProperties":false,"properties":{"id":{"type":"string"},"name":{"type":["null","string"]},"plan":{"type":["null","string"]}}},"scopes":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}}}}},"/api/positions":{"post":{"summary":"Ingest one GPS position fix.","tags":["ingest"],"description":"Authenticated mobile devices POST one telemetry sample. The server authenticates the JWT, validates the body, stamps server_received_at, produces to loc_mesh.telemetry.tracking.v1, and returns 202. Returns 401 on auth failure, 400 on schema failure, 403 if the body driverId / deviceId conflict with the verified JWT claims (cross-driver spoofing).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["driverId","clientTs","lat","lon"],"additionalProperties":false,"properties":{"driverId":{"type":"string","minLength":1,"description":"Driver identifier as known to the mobile app. Compared against the driverId claim on the JWT; mismatch is rejected to prevent cross-driver spoofing."},"deviceId":{"type":"string","minLength":1,"description":"Optional device identifier. If supplied and the JWT carries a device_id claim, they must match."},"clientTs":{"type":"string","format":"date-time","description":"When the device captured the fix (ISO-8601, device clock)."},"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in WGS84."},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in WGS84."},"accuracyM":{"type":"number","minimum":0,"description":"Horizontal accuracy estimate in metres."},"speedMps":{"type":"number","minimum":0,"description":"Reported speed in metres per second."},"headingDeg":{"type":"number","minimum":0,"maximum":360,"description":"Reported heading, 0-360 degrees."},"altitudeM":{"type":"number","description":"Reported altitude in metres."}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["accepted","serverReceivedAt"],"additionalProperties":false,"properties":{"accepted":{"type":"boolean"},"serverReceivedAt":{"type":"string"}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}},"415":{"description":"The request body was not sent as application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope","description":"The request body was not sent as application/json."}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/envelope"}}}}}}}},"servers":[{"url":"https://ingest-api.mesh.locate2u.app"}]}