{"openapi":"3.1.0","info":{"title":"uEngage Zones API","version":"0.1.0","description":"Generic, domain-agnostic spatial primitive: tagged polygons + point-in-polygon containment queries. Knows nothing about businesses or serviceability - those layer on top via a separate consumer.","license":{"name":"Proprietary"}},"servers":[{"url":"https://api.platform.uengage.io","description":"Production"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"OAuth2 Bearer JWT, RS256-signed by the auth service. Verify against the JWKS at `/auth/business/jwks.json`. Service-to-service callers mint via `POST /auth/business/oauth/token` with `grant_type=client_credentials`."}},"schemas":{"TagBag":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"ZoneCreateResult":{"type":"object","properties":{"zid":{"type":"string","format":"uuid"},"tags":{"$ref":"#/components/schemas/TagBag"},"createdAt":{"type":"string"}},"required":["zid","tags","createdAt"]},"ApiError":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"nullable":true}}},"required":["error"],"description":"Standard error envelope. `error` is a stable machine-readable code; `message` is a human-readable hint; `issues` (when present) is a Zod validation issue list."},"GeoJsonMultiPolygon":{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":2}}}}},"required":["type","coordinates"]},"Zone":{"type":"object","properties":{"zid":{"type":"string","format":"uuid"},"geometry":{"$ref":"#/components/schemas/GeoJsonMultiPolygon"},"tags":{"$ref":"#/components/schemas/TagBag"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["zid","geometry","tags","createdAt","updatedAt"]},"ZoneSummary":{"type":"object","properties":{"zid":{"type":"string","format":"uuid"},"tags":{"$ref":"#/components/schemas/TagBag"}},"required":["zid","tags"]},"ZoneListPage":{"type":"object","properties":{"zones":{"type":"array","items":{"$ref":"#/components/schemas/ZoneSummary"}},"nextCursor":{"type":"string","nullable":true}},"required":["zones","nextCursor"]}},"parameters":{}},"paths":{"/v1/zones":{"post":{"tags":["zones"],"summary":"Create a zone","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"geometry":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["Polygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1},"minItems":1}},"required":["type","coordinates"]}]},"tags":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]},"default":{}}},"required":["geometry"]}}}},"responses":{"201":{"description":"created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneCreateResult"}}}},"400":{"description":"invalid geometry or tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"tags":["zones"],"summary":"List zones (tag-filtered, keyset-paginated)","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"tags","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"page of zones","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneListPage"}}}},"400":{"description":"invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/zones/{zid}":{"get":{"tags":["zones"],"summary":"Get a zone by id","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"zid","in":"path"}],"responses":{"200":{"description":"zone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"}}}},"400":{"description":"zid is not a uuid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"zone not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"tags":["zones"],"summary":"Partial update (JSON Merge Patch)","description":"Tags are shallow-merged: provided keys overwrite, `null` deletes, absent keys are unchanged. Merge is applied atomically (`tags = (tags || :setPatch) - :nullKeys`) so concurrent PATCHes to different keys both persist.","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"zid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"geometry":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["Polygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1},"minItems":1}},"required":["type","coordinates"]}]},"tags":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}},{"nullable":true},{"nullable":true}]}}}}}}},"responses":{"200":{"description":"updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"}}}},"400":{"description":"invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"zone not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"tags":["zones"],"summary":"Full replace of geometry + tags","description":"Replaces both fields. Omitted or empty `tags` clears all tags.","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"zid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"geometry":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["Polygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1},"minItems":1}},"required":["type","coordinates"]}]},"tags":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]},"default":{}}},"required":["geometry"]}}}},"responses":{"200":{"description":"replaced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"}}}},"400":{"description":"invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"zone not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"tags":["zones"],"summary":"Delete a zone","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"zid","in":"path"}],"responses":{"204":{"description":"deleted"},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"zone not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/zones/batch/create":{"post":{"tags":["zones"],"summary":"Create many zones in one call","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"type":"object","properties":{"geometry":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["Polygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"anyOf":[{"type":"number","minimum":-180,"maximum":180},{"type":"number","minimum":-90,"maximum":90}]},"minItems":2,"maxItems":2},"minItems":4},"minItems":1},"minItems":1}},"required":["type","coordinates"]}]},"tags":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]},"default":{}}},"required":["geometry"]},"minItems":1,"maxItems":500}},"required":["zones"]}}}},"responses":{"201":{"description":"created","content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"type":"object","properties":{"zid":{"type":"string","format":"uuid"}},"required":["zid"]}}},"required":["zones"]}}}},"400":{"description":"invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/zones/batch/delete":{"post":{"tags":["zones"],"summary":"Delete many zones in one call","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"zids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":1000}},"required":["zids"]}}}},"responses":{"200":{"description":"deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"}},"required":["deleted"]}}}},"400":{"description":"invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"caller is not a service-actor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/zones/containing":{"post":{"tags":["zones"],"summary":"Containment query - zones that contain a point","description":"Returns zones whose geometry contains the supplied point (boundary-inclusive via PostGIS `ST_Covers`). `ids` and `tags` selectors are optional and AND-ed when both present. With neither selector, returns all zones containing the point.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"point":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"]},"ids":{"type":"array","items":{"type":"string","format":"uuid"}},"tags":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}]}}},"required":["point"]}}}},"responses":{"200":{"description":"matching zones","content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"$ref":"#/components/schemas/ZoneSummary"}}},"required":["zones"]}}}},"400":{"description":"invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"missing or invalid bearer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}}}