{"openapi":"3.1.0","info":{"title":"bytEM API","version":"1.0.0","description":"bytEM API provides access to the bytEM data exchange functionality.\nThis document is the agent-facing surface: the operations an autonomous caller may use, in the order it uses them. Read: getIndexSummary first, then listIndex / searchIndex, getDeidStatus and getReferences (no identity). Obtain: createGuestUser (or bring a Matrix user), then findByDeid to learn what is available and how. Receive: requestDeidAccess, poll getDeidAccessStatus, read fetchExchangeEvents; or getAgentAccess, which chains those behind one GET for a caller that cannot POST. Three operations create state on this market and are marked x-side-effect: a guest identity, a demand room, or both.\nNot on this surface, by design: account registration and passwords, room creation and editing, seeding and asset publishing, public index rebuilds, administration and federation receive hooks. Those belong to people operating a market and are documented in openapi-internal.yaml.\nCompatibility policy: Minor versions may add optional fields or endpoints without breaking existing clients. Changes to required request or response fields, authentication requirements, or API semantics that break existing clients require a new major API version.\n"},"x-oudea-protocol-version":"0.1","x-bytem-api-version":"1","servers":[{"url":"https://bytem.de.cities.app","description":"This bytEM instance"}],"tags":[{"name":"Index","description":"Public data index, filtering, search, and summaries"},{"name":"DEID","description":"DEID status and metadata"},{"name":"Search","description":"Find data associated with a DEID"},{"name":"References","description":"Resolve DEID references"},{"name":"Demand","description":"Request and monitor demand data"},{"name":"Auth","description":"Guest identity for reading public demand rooms"},{"name":"Room","description":"Read demand room state and the events exchanged into it"}],"paths":{"/api/index":{"get":{"tags":["Index"],"summary":"List indexed data","description":"Returns entries from the public bytEM index.\n\nSupports filtering, field projection, sorting, quality filtering,\noptional infrastructure/quality details, and pagination.\n","operationId":"listIndex","parameters":[{"name":"deid","in":"query","required":false,"description":"Filter by DEID.","schema":{"type":"string"},"example":"https://cities.app/de/rp/aach"},{"name":"domain","in":"query","required":false,"description":"Filter by domain.","schema":{"type":"string"},"example":"cities.app"},{"name":"class","in":"query","required":false,"description":"Filter by class URI.","schema":{"type":"string"},"example":"https://cities.app/class/City"},{"name":"room_type","in":"query","required":false,"description":"Filter by room type.","schema":{"type":"string"},"example":"entity"},{"name":"market_status","in":"query","required":false,"description":"Filter by market status.","schema":{"type":"string"},"example":"exchangeable"},{"name":"group","in":"query","required":false,"description":"Filter by group.","schema":{"type":"string"}},{"name":"depth","in":"query","required":false,"description":"Filter by hierarchy depth.","schema":{"type":"integer"}},{"name":"min_source_quality","in":"query","required":false,"description":"Minimum source quality score.","schema":{"type":"number"}},{"name":"max_source_quality","in":"query","required":false,"description":"Maximum source quality score.","schema":{"type":"number"}},{"name":"bbox","in":"query","required":false,"description":"Geographic bounding box filter.","schema":{"type":"string"},"example":"6.5,49.7,6.7,49.9"},{"name":"sort","in":"query","required":false,"description":"Field used for sorting.","schema":{"type":"string","enum":["source_quality","label","deid","class","domain","market_status","room_type"]},"example":"label"},{"name":"order","in":"query","required":false,"description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"]},"default":"asc"},{"name":"fields","in":"query","required":false,"description":"Comma-separated list of fields to return for each index entry.\n","schema":{"type":"string"},"example":"deid,label,class,domain,room_type,market_status"},{"name":"include","in":"query","required":false,"description":"Comma-separated optional details to include.\n","schema":{"type":"string"},"example":"infra,quality"},{"name":"limit","in":"query","required":false,"description":"Number of entries to return.","schema":{"type":"integer","minimum":1},"example":100},{"name":"offset","in":"query","required":false,"description":"Number of entries to skip.","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"Index fetched successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexApiResponse"},"example":{"statusCode":200,"data":{"index_source":"solr","filters":{"sort":"label","order":"asc"},"total":2,"offset":0,"limit":100,"returned":2,"entries":[{"deid":"https://cities.app/de/rp/aach","label":"Aach","class":"https://cities.app/class/City","domain":"cities.app","room_type":"entity","market_status":"exchangeable","source_quality":85,"source_quality_status":"scored","location":{"lat":49.789601,"lon":6.590527,"location_mode":"fixed"}},{"deid":"https://cities.app/de/rp/test","label":"Test","class":"https://cities.app/class/City","domain":"cities.app","room_type":"entity","market_status":"exchangeable","source_quality":null,"source_quality_status":"unavailable"}]},"message":"Index fetched successfully","success":true}}}}}}},"/api/index/search":{"get":{"tags":["Index"],"summary":"Search the public index","description":"Searches indexed entries using free-text search and the same filtering,\nsorting, field projection, and pagination capabilities as the index API.\n","operationId":"searchIndex","parameters":[{"name":"q","in":"query","required":false,"description":"Free-text search query.","schema":{"type":"string"},"example":"city"},{"name":"deid","in":"query","required":false,"schema":{"type":"string"}},{"name":"domain","in":"query","required":false,"schema":{"type":"string"}},{"name":"class","in":"query","required":false,"schema":{"type":"string"}},{"name":"room_type","in":"query","required":false,"schema":{"type":"string"}},{"name":"market_status","in":"query","required":false,"schema":{"type":"string"}},{"name":"group","in":"query","required":false,"schema":{"type":"string"}},{"name":"depth","in":"query","required":false,"schema":{"type":"integer"}},{"name":"min_source_quality","in":"query","required":false,"schema":{"type":"number"}},{"name":"max_source_quality","in":"query","required":false,"schema":{"type":"number"}},{"name":"bbox","in":"query","required":false,"schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["source_quality","label","deid","class","domain","market_status","room_type"]}},{"name":"order","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"]}},{"name":"fields","in":"query","required":false,"description":"Comma-separated fields to return.","schema":{"type":"string"},"example":"deid,label,class,domain,room_type,market_status"},{"name":"include","in":"query","required":false,"description":"Comma-separated optional details.","schema":{"type":"string"},"example":"infra,quality"},{"name":"limit","in":"query","required":false,"description":"Number of entries to return.","schema":{"type":"integer","minimum":1},"example":100},{"name":"offset","in":"query","required":false,"description":"Number of entries to skip.","schema":{"type":"integer","minimum":0},"example":0}],"responses":{"200":{"description":"Index search completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexSearchApiResponse"},"example":{"statusCode":200,"data":{"query":"city","filters":{"sort":"label","order":"asc"},"total":10747,"offset":0,"limit":100,"returned":2,"entries":[{"deid":"https://cities.app/de/rp/aach","label":"Aach","class":"https://cities.app/class/City","domain":"cities.app","room_type":"entity","market_status":"exchangeable","source_quality":85,"source_quality_status":"scored","location":{"lat":49.789601,"lon":6.590527,"location_mode":"fixed"}},{"deid":"https://cities.app/de/rp/test","label":"Test","class":"https://cities.app/class/City","domain":"cities.app","room_type":"entity","market_status":"exchangeable"}]},"message":"Index search completed","success":true}}}}}}},"/api/index/summary":{"get":{"tags":["Index"],"summary":"Get an index summary","description":"Returns summary information about the public bytEM index.","operationId":"getIndexSummary","parameters":[{"name":"domain","in":"query","required":false,"schema":{"type":"string"},"example":"cities.app"},{"name":"class","in":"query","required":false,"schema":{"type":"string"},"example":"https://cities.app/class/City"}],"responses":{"200":{"description":"Index summary fetched successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexSummaryApiResponse"},"example":{"statusCode":200,"data":{"@context":"https://bytem.app/index/summary","total":2,"date_modified":"2026-10-04T12:41:07.000Z","deid_prefix":"https://cities.app/de/","deid_suffix":"","deid_pattern":"https://cities.app/de/{item}","pattern_coverage":2,"examples":["https://cities.app/de/rp/aach","https://cities.app/de/rp/test"],"counts":{"room_types":{"entity":2},"market_statuses":{"exchangeable":2},"supply_types":{"supply-type-dataset":2,"supply-type-reference":1},"versions":{"release=2026-1788867132427":1,"uploaded=2026-09-08":1}},"classes":{"https://cities.app/class/City":{"count":2}},"source_quality":{"average":null,"scored":0,"total":2,"coverage_percent":0,"criteria_version":"dq-v002"},"fields":["base_type","class","deid","deid_document","domain","entity_status","label","market_status","room_type","source_checked_at","source_quality","source_quality_status","version","schema"],"endpoints":{"search":"/api/index/search?q={text}","get":"/api/index?deid={deid}","list":"/api/index?class={class}"},"access":{"index":{"question":"What exists?","identity":"none","endpoint":"/api/index"},"deid":{"question":"What is this entity?","identity":"none","endpoint":"{deid}"},"find":{"question":"What can I access, and how?","identity":"matrix_user","endpoint":"/api/search/find-by-deid?deid={deid}"},"exchange":{"question":"Give me the actual data","identity":"matrix_user","endpoint":"POST /api/demand/access-by-deid"},"identity_note":"A Matrix user on this homeserver, matrix.org, or your own — see /how-bytem-works.","markets":"https://bytem.app/markets/bytem-market-list.json"},"auto_reindex":{"scanned":0,"drifted":0,"queued":0,"remaining":0,"at":"2026-09-08T11:30:15.654Z"}},"message":"Index summary fetched successfully","success":true}}}}}}},"/api/deid/status":{"get":{"tags":["DEID"],"summary":"Get DEID status","description":"Returns the availability and metadata status for a DEID.\n","operationId":"getDeidStatus","parameters":[{"name":"deid","in":"query","required":true,"description":"DEID to inspect.","schema":{"type":"string","format":"uri"},"example":"https://cities.app/de/he/aarbergen"}],"responses":{"200":{"description":"DEID status returned successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeidStatusApiResponse"},"example":{"statusCode":200,"data":{"exists":true,"roomExists":true,"domainExists":true,"classPresent":true,"schemaPresent":true,"accessState":"ready","statusMessage":"Domain document and bytEM room are both available.","lookupWarning":null,"domainUrl":"https://cities.app/de/he/aarbergen","label":"Aarbergen","domain":"cities.app","classUrl":"https://cities.app/class/City","schemaUrl":"https://cities.app/schema/city","meta":{"lastModified":"2026-08-27T18:57:58.686Z","core":"entity"},"location":{"lat":50.24602,"lon":8.076766,"location_mode":"fixed"},"domainLocation":{"lat":50.24602,"lon":8.076766,"location_mode":"fixed"},"locationMode":"fixed","coverageGeojsonUrl":null,"product":null},"message":"Success","success":true}}}},"400":{"description":"Missing DEID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/search/find-by-deid":{"get":{"tags":["Search"],"summary":"Find data by DEID","description":"Finds indexed supply data associated with a DEID.\n\nA Matrix access token is required.\n","operationId":"findByDeid","security":[{"matrixBearerAuth":[]}],"parameters":[{"name":"deid","in":"query","required":true,"description":"DEID to find.","schema":{"type":"string","format":"uri"},"example":"https://cities.app/de/by/alfeld/water/drinking-water-quality"},{"name":"base_type","in":"query","required":false,"description":"Optional base type filter.","schema":{"type":"string"},"example":"supply"}],"responses":{"200":{"description":"Data matching the DEID was found successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindByDeidApiResponse"},"example":{"statusCode":200,"data":[{"core":"dataset","deid":"https://cities.app/de/by/alfeld/water/drinking-water-quality","domain":"cities.app","base_type":"supply","room_type":"dataset","class":"https://cities.app/class/water/DrinkingWaterQuality","class_uri":"https://cities.app/class/water/DrinkingWaterQuality","entity_status":"registered","market_status":"exchangeable","source_quality":100,"source_quality_status":"scored","source_checked_at":"2026-08-27T18:21:42.416Z","source_quality_criteria":"dq-v002","dataset_assets":"[{\"asset_key\":\"trinkwasseranalyse\",\"deid\":\"https://cities.app/de/by/alfeld/water/drinking-water-quality\",\"schema\":\"https://cities.app/schema/water/drinking-water-quality\",\"supply_type\":\"supply-type-dataset\",\"versions\":[{\"event_id\":\"$U_1Gc8e1SGxjfQc0PE_HR5C4TGh20U3lheQZhWtV6Gc\",\"version_key\":\"2025\"}]}]","api_assets":"[]","id":"!QHQceyRcuDHQMPqHUk:matrix.bytem.alpha.cities.app","room_id":"!QHQceyRcuDHQMPqHUk:matrix.bytem.alpha.cities.app","room_name":"Alfeld — Trinkwasserqualität","room_alias":"#alfeld-—-trinkwasserqualität_supply_room.type:matrix.bytem.alpha.cities.app"}],"numRecords":1,"message":"DEID found successfully","success":true}}}},"400":{"description":"DEID query parameter is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"No Matrix access token was presented, or the homeserver rejected the one that was. Find is gated on room membership and power level, so an unauthenticated caller is refused here rather than served a filtered result.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No supply room or DEID document was found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The find document store did not answer and no other market held the DEID, so whether it has a supply room here could not be determined. Not the same answer as 404.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/references":{"get":{"tags":["References"],"summary":"Resolve DEID references","description":"Resolves outgoing and/or incoming references for a DEID.\n\nThe direction can be `out`, `in`, or `both`.\n","operationId":"getReferences","parameters":[{"name":"deid","in":"query","required":true,"description":"Source DEID.","schema":{"type":"string"},"example":"https://cities.app/de/rp/aach"},{"name":"direction","in":"query","required":false,"description":"Reference direction.","schema":{"type":"string","enum":["out","in","both"],"default":"out"}}],"responses":{"200":{"description":"References resolved successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferencesApiResponse"},"example":{"statusCode":200,"data":{"source_deid":"https://cities.app/de/rp/aach","direction":"both","references":[{"target_deid":"https://cities.app/de/rp/test","label":"Test","kind":"demand","role":"related"}],"referenced_by":[{"source_deid":"https://cities.app/de/rp/source","label":"Source","kind":"supply","role":null}],"note":"supply-type-reference edges only; demand_references live in the referring domain's DEID document and are not enumerable here"},"message":"References resolved successfully","success":true}}}},"400":{"description":"Invalid or missing DEID/direction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/demand/access-by-deid":{"post":{"tags":["Demand"],"summary":"Request access to a DEID","description":"Queues a request to create/access a demand room for the supplied DEID.\n\n`matrix_user` is required. `email` is optional.\n\nThe Matrix user may be supplied in one of these forms:\n- `@user:server`\n- `user:server`\n- `user@server`\n\nThe API normalizes the value to `@user:server`.\n\nAn `Idempotency-Key` header may be supplied to safely retry the request.\n","operationId":"requestDeidAccess","x-side-effect":"Opens or reuses a demand room for the DEID and queues an exchange. Send Idempotency-Key when retrying.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional idempotency key. Maximum length is 200 characters.\n","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeidAccessRequest"},"example":{"deid":"https://cities.app/de/rp/aach","matrix_user":"@test_bytem_matrix_org:matrix.org","email":"optional@example.com","location":{"lat":49.789601,"lon":6.590527}}}}},"responses":{"200":{"description":"DEID access request queued successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeidAccessApiResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Idempotency key was already used with different request data.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Demand request rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error while processing the DEID access request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The message queue behind this request is unavailable; nothing was queued. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/demand/access-by-deid-status/{request_id}":{"get":{"tags":["Demand"],"summary":"Get DEID access request status","description":"Returns the current status of a previously submitted DEID access request.","operationId":"getDeidAccessStatus","parameters":[{"name":"request_id","in":"path","required":true,"description":"Request identifier returned by the access request endpoint.","schema":{"type":"string"},"example":"f8ea8dd9-3eb8-4c96-9b74-b3a042bdae18"}],"responses":{"200":{"description":"Request status fetched successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeidAccessStatusApiResponse"},"example":{"statusCode":200,"data":{"status":"queued"},"message":"Request status fetched successfully","success":true}}}},"404":{"description":"Request status not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/demand/agent-access":{"get":{"tags":["Demand"],"summary":"Guest access + exchange, behind one GET","description":"Runs access-by-deid, a short status poll, createGuestUser and a read\nof the delivered room behind one followable link. Exists for a caller\nthat can follow a hyperlink and cannot issue a POST or drive a JS\nsession — see llms.txt's \"no POST, no JS\" note. An API-capable caller\nshould call requestDeidAccess, getDeidAccessStatus, createGuestUser,\ngetPublicRoomState and fetchExchangeEvents directly instead.\n\nAn exchange takes a few seconds longer than this call waits, so the\nfirst answer is usually a 202 whose `retry_url` is this route with the\n`request_id` filled in. Following it does not queue a new access; it\nresumes the same one and answers 200 once the exchange is done.\n\nMints a real guest identity on each 200 answer; a 202 mints none.\nAccess-by-deid reuses an existing room for the DEID rather than creating a new one, which is\nwhat keeps a repeated or accidental hit on this GET inexpensive.\n","operationId":"getAgentAccess","x-side-effect":"Mints a guest and opens a demand room in one call. Repeat with request_id to poll, not to start again.","parameters":[{"name":"deid","in":"query","required":true,"description":"The DEID to access.","schema":{"type":"string"},"example":"https://cities.app/de/he/eppstein"},{"name":"lat","in":"query","required":false,"description":"WGS84 latitude of the point of interest, given with `lon`. Needed when the DEID document says `location_mode` `user_input`; the exchange then runs in its own demand room for that point.","schema":{"type":"number","minimum":-90,"maximum":90},"example":51.9367},{"name":"lon","in":"query","required":false,"description":"WGS84 longitude of the point of interest, given with `lat`.","schema":{"type":"number","minimum":-180,"maximum":180},"example":8.8789},{"name":"request_id","in":"query","required":false,"description":"Resume an exchange this route already queued, as carried by a 202's `retry_url`.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The exchange is complete and the delivered events are in the body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentAccessApiResponse"}}}},"202":{"description":"The exchange is still running. Fetch `data.retry_url` and repeat\nuntil it answers 200. `data.status_url` is the raw status endpoint\naccess-by-deid callers poll, for a caller that wants the stages.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentAccessPendingApiResponse"}}}},"400":{"description":"Missing deid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The request_id is not one this instance knows; request access again without it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"A step in the sequence (access request, guest creation, or the exchange itself) failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/index/demand":{"get":{"tags":["Index"],"summary":"List shared demand rooms","description":"Returns shared demand rooms from the public demand index, ordered from newest to oldest. Optionally filters results by DEID and supports pagination using limit and offset.\n","operationId":"getDemandIndex","parameters":[{"name":"deid","in":"query","required":false,"description":"Filter results to a single DEID.","schema":{"type":"string"},"example":"https://cities.app/deid/example"},{"name":"limit","in":"query","required":false,"description":"Maximum number of entries to return. When omitted, a default client page size is used unless filtering by DEID.\n","schema":{"type":"integer","minimum":1},"example":20},{"name":"offset","in":"query","required":false,"description":"Number of entries to skip before returning results.","schema":{"type":"integer","minimum":0,"default":0},"example":0}],"responses":{"200":{"description":"Demand index fetched successfully.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","required":["index_room_id","total","offset","limit","returned","entries"],"properties":{"index_room_id":{"type":"string","description":"Matrix room ID of the demand index room.","example":"!index123:matrix.example.org"},"total":{"type":"integer","description":"Total number of entries matching the DEID filter before pagination.","example":42},"offset":{"type":"integer","example":0},"limit":{"type":["integer","null"],"description":"Requested page size, or null when no limit is applied.","example":20},"returned":{"type":"integer","description":"Number of entries returned in this response.","example":20},"entries":{"type":"array","description":"Demand index entries ordered newest first.","items":{"type":"object","additionalProperties":true}}}}}}]},"example":{"statusCode":200,"data":{"index_room_id":"!index123:matrix.example.org","total":42,"offset":0,"limit":20,"returned":20,"entries":[]},"message":"Demand index fetched successfully","success":true}}}}}}},"/api/auth/createGuestUser":{"post":{"tags":["Auth"],"summary":"Create a guest Matrix user","description":"Creates a Matrix guest user and establishes a bytEM session. If room_id\nis supplied, the guest joins that room (a failed join fails the request\nwith 502) and the response carries the room's canonical alias, when it\nhas one.\n","operationId":"createGuestUser","x-side-effect":"Mints a Matrix guest identity on this homeserver. Reuse the token; do not call per request.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"room_id":{"type":"string","description":"Matrix room ID to automatically join.","example":"!exampleRoomId:matrix.org"}}},"example":{"room_id":"!exampleRoomId:matrix.org"}}}},"responses":{"200":{"description":"Guest user created successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"},"example":{"statusCode":200,"data":{"user":{"matrix_id":"@guest123:matrix.org","displayName":"guest123","email":null,"token":"guest_access_token","home_server":"https://matrix.example.org","_id":"@guest123:matrix.org","sessionId":"abc123","role":"guest"},"roomAlias":"#example:matrix.org","joined":true},"message":"Guest User Created Successfully","success":true}}}},"403":{"description":"Guest login is not allowed by the Matrix homeserver.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Guest authentication or session error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Guest user was created but automatic room joining failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/room/roomStates/public/{roomId}":{"get":{"tags":["Room"],"summary":"Get public room state","description":"Returns the complete Matrix state for a world-readable room using the local bot credentials. The room must expose world-readable history.\n","operationId":"getPublicRoomState","parameters":[{"name":"roomId","in":"path","required":true,"description":"Matrix room ID.","schema":{"type":"string"},"example":"!abc123:matrix.example.org"}],"responses":{"200":{"description":"Room state fetched successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}},"403":{"description":"Room state is not world readable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Public room state is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/room/stateEvents/exchange":{"post":{"tags":["Room"],"summary":"Fetch exchange events","description":"Returns the events for the event IDs in the body. Take the IDs from the\ndemand room's descriptor state events (getPublicRoomState). Send roomId\ntoo: a local room is then read with the bot's token, and a federated\nroom is joined before the read.\n","operationId":"fetchExchangeEvents","security":[{"matrixBearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","properties":{"eventIds":{"type":"array","items":{"type":"string"}},"roomId":{"type":"string"}},"required":["eventIds"]}]}}}},"responses":{"200":{"description":"Exchange events fetched successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}},"401":{"description":"No Matrix access token was presented, or the homeserver rejected it. A guest token from /api/auth/createGuestUser is sufficient for a room that guest has joined.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"matrixBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Matrix access token","description":"Matrix access token."}},"schemas":{"Location":{"type":"object","properties":{"lat":{"type":"number","example":50.24602},"lon":{"type":"number","example":8.076766},"location_mode":{"type":"string","example":"fixed"}},"additionalProperties":false},"ApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"type":"object","additionalProperties":true},"message":{"type":"string"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"IndexEntry":{"type":"object","required":["deid","label","class","domain","room_type","market_status","source_quality","source_quality_status","location"],"properties":{"deid":{"type":"string","format":"uri","example":"https://cities.app/de/rp/aach"},"label":{"type":"string","maxLength":128,"example":"Aach"},"class":{"type":"string","format":"uri","example":"https://cities.app/class/City"},"domain":{"type":"string","example":"cities.app"},"room_type":{"type":"string","example":"entity"},"market_status":{"type":"string","example":"exchangeable"},"source_quality":{"oneOf":[{"type":"number"},{"type":"null"}],"example":85},"source_quality_status":{"type":"string","example":"scored"},"location":{"oneOf":[{"$ref":"#/components/schemas/Location"},{"type":"null"}]}},"additionalProperties":false},"IndexResponseData":{"type":"object","required":["index_source","filters","total","offset","limit","returned","entries"],"properties":{"index_source":{"type":"string","example":"solr"},"filters":{"type":"object","additionalProperties":true},"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"oneOf":[{"type":"integer"},{"type":"null"}]},"returned":{"type":"integer"},"entries":{"type":"array","items":{"$ref":"#/components/schemas/IndexEntry"}}},"additionalProperties":false},"IndexApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"$ref":"#/components/schemas/IndexResponseData"},"message":{"type":"string","example":"Index fetched successfully"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"IndexSearchResponseData":{"type":"object","required":["query","filters","total","offset","limit","returned","entries"],"properties":{"query":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"city"},"filters":{"type":"object","properties":{"sort":{"type":"string","enum":["source_quality","label","deid","class","domain","market_status","room_type"],"example":"label"},"order":{"type":"string","enum":["asc","desc"],"example":"asc"}},"additionalProperties":false},"total":{"type":"integer","example":10747},"offset":{"type":"integer","minimum":0,"example":0},"limit":{"type":"integer","minimum":1,"example":100},"returned":{"type":"integer","minimum":0,"example":100},"entries":{"type":"array","items":{"$ref":"#/components/schemas/IndexEntry"}}},"additionalProperties":false},"IndexSearchApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"$ref":"#/components/schemas/IndexSearchResponseData"},"message":{"type":"string","example":"Index search completed"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"ClassSummary":{"type":"object","required":["count"],"properties":{"count":{"type":"integer","example":10747},"keywords":{"type":"array","items":{"type":"string"},"example":["drinking water","water quality","Trinkwasser","municipal","TrinkwV","lab analysis","water parameters","Germany"]}},"additionalProperties":false},"SourceQualitySummary":{"type":"object","properties":{"average":{"oneOf":[{"type":"number"},{"type":"null"}],"example":87},"scored":{"type":"integer","example":12723},"total":{"type":"integer","example":12723},"coverage_percent":{"type":"number","example":100},"criteria_version":{"type":"string","example":"dq-v002"}},"additionalProperties":false},"IndexSummary":{"type":"object","required":["@context","total","deid_prefix","deid_suffix","deid_pattern","pattern_coverage","examples","counts","classes","graph","source_quality","fields","endpoints","access","auto_reindex"],"properties":{"@context":{"type":"string","format":"uri","example":"https://bytem.app/index/summary"},"total":{"type":"integer","example":12723},"date_modified":{"type":"string","format":"date-time","description":"When the indexed content last changed: the latest time an entry was (re)indexed or its source rescored. Absent when no entry carries a timestamp.","example":"2026-10-04T12:41:07.000Z"},"deid_prefix":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"https://cities.app/de/"},"deid_suffix":{"oneOf":[{"type":"string"},{"type":"null"}],"example":""},"deid_pattern":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"https://cities.app/de/{group}/{item}"},"pattern_coverage":{"type":"integer","example":10747},"examples":{"type":"array","items":{"type":"string","format":"uri"},"example":["https://cities.app/de/bb/ahrensfelde","https://cities.app/de/bb/alt-tucheband"]},"counts":{"type":"object","properties":{"groups":{"type":"object","additionalProperties":{"type":"integer"},"example":{"rp":2560,"by":2474,"bw":1468}},"room_types":{"type":"object","additionalProperties":{"type":"integer"},"example":{"entity":10747,"dataset":1976}},"market_statuses":{"type":"object","additionalProperties":{"type":"integer"},"example":{"exchangeable":12723}},"supply_types":{"type":"object","additionalProperties":{"type":"integer"},"example":{"supply-type-json":10747,"supply-type-dataset":1976,"supply-type-html":1976,"supply-type-reference":926}},"schemas":{"type":"object","additionalProperties":{"type":"integer"},"example":{"https://cities.app/schema/city":10747,"https://cities.app/schema/water/drinking-water-quality":1976}},"versions":{"type":"object","additionalProperties":{"type":"integer"},"example":{"2025":1976,"2024-12-31":10747,"current":926}}},"additionalProperties":false},"classes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ClassSummary"}},"graph":{"type":"object","properties":{"role_counts":{"type":"object","additionalProperties":{"type":"integer"},"example":{"benchmark":1976}},"most_referenced":{"type":"array","items":{"type":"object","required":["deid","referenced_by"],"properties":{"deid":{"type":"string","format":"uri"},"referenced_by":{"type":"integer"}},"additionalProperties":false}}},"additionalProperties":false},"source_quality":{"$ref":"#/components/schemas/SourceQualitySummary"},"fields":{"type":"array","items":{"type":"string"}},"endpoints":{"type":"object","properties":{"search":{"type":"string"},"get":{"type":"string"},"list":{"type":"string"}},"additionalProperties":false},"access":{"type":"object","properties":{"index":{"type":"object","properties":{"question":{"type":"string"},"identity":{"type":"string"},"endpoint":{"type":"string"}},"additionalProperties":false},"deid":{"type":"object","properties":{"question":{"type":"string"},"identity":{"type":"string"},"endpoint":{"type":"string"}},"additionalProperties":false},"find":{"type":"object","properties":{"question":{"type":"string"},"identity":{"type":"string"},"endpoint":{"type":"string"}},"additionalProperties":false},"exchange":{"type":"object","properties":{"question":{"type":"string"},"identity":{"type":"string"},"endpoint":{"type":"string"}},"additionalProperties":false},"identity_note":{"type":"string"},"markets":{"type":"string","format":"uri"}},"additionalProperties":false},"auto_reindex":{"type":"object","properties":{"scanned":{"type":"integer"},"drifted":{"type":"integer"},"queued":{"type":"integer"},"remaining":{"type":"integer"},"at":{"type":"string","format":"date-time"}},"additionalProperties":false}},"additionalProperties":false},"IndexSummaryApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"$ref":"#/components/schemas/IndexSummary"},"message":{"type":"string","example":"Index summary fetched successfully"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"DeidStatus":{"type":"object","required":["exists","roomExists","domainExists","classPresent","schemaPresent","accessState","statusMessage","lookupWarning","domainUrl","label","domain","classUrl","schemaUrl","meta","location","domainLocation","locationMode","coverageGeojsonUrl","product"],"properties":{"exists":{"type":"boolean","example":true},"roomExists":{"type":"boolean","example":true},"domainExists":{"type":"boolean","example":true},"classPresent":{"type":"boolean","example":true},"schemaPresent":{"type":"boolean","example":true},"accessState":{"type":"string","example":"ready"},"statusMessage":{"type":"string","example":"Domain document and bytEM room are both available."},"lookupWarning":{"oneOf":[{"type":"string"},{"type":"null"}],"example":null},"domainUrl":{"oneOf":[{"type":"string","format":"uri"},{"type":"null"}],"example":"https://cities.app/de/he/aarbergen"},"label":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"Aarbergen"},"domain":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"cities.app"},"classUrl":{"oneOf":[{"type":"string","format":"uri"},{"type":"null"}],"example":"https://cities.app/class/City"},"schemaUrl":{"oneOf":[{"type":"string","format":"uri"},{"type":"null"}],"example":"https://cities.app/schema/city"},"meta":{"oneOf":[{"type":"object","properties":{"lastModified":{"type":"string","format":"date-time","example":"2026-08-27T18:57:58.686Z"},"core":{"type":"string","example":"entity"}},"additionalProperties":false},{"type":"null"}]},"location":{"oneOf":[{"$ref":"#/components/schemas/Location"},{"type":"null"}]},"domainLocation":{"oneOf":[{"$ref":"#/components/schemas/Location"},{"type":"null"}]},"locationMode":{"oneOf":[{"type":"string"},{"type":"null"}],"example":"fixed"},"coverageGeojsonUrl":{"oneOf":[{"type":"string","format":"uri"},{"type":"null"}],"example":null},"product":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","items":{}},{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}],"example":null}},"additionalProperties":false},"DeidStatusApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"$ref":"#/components/schemas/DeidStatus"},"message":{"type":"string","example":"Success"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"FindByDeidApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer","example":200},"data":{"type":"array","required":["core","deid","domain","base_type","room_type","class","class_uri","entity_status","market_status","source_quality","source_quality_status","source_checked_at","source_quality_criteria","dataset_assets","api_assets","id","room_id","room_name","room_alias","numRecords"],"properties":{"core":{"type":"string","example":"dataset"},"deid":{"type":"string","format":"uri","example":"https://cities.app/de/by/alfeld/water/drinking-water-quality"},"domain":{"type":"string","example":"cities.app"},"base_type":{"type":"string","example":"supply"},"room_type":{"type":"string","example":"dataset"},"class":{"type":"string","format":"uri","example":"https://cities.app/class/water/DrinkingWaterQuality"},"class_uri":{"type":"string","format":"uri","example":"https://cities.app/class/water/DrinkingWaterQuality"},"entity_status":{"type":"string","example":"registered"},"market_status":{"type":"string","example":"exchangeable"},"source_quality":{"type":"number","example":100},"source_quality_status":{"type":"string","example":"scored"},"source_checked_at":{"type":"string","format":"date-time","example":"2026-08-27T18:21:42.416Z"},"source_quality_criteria":{"type":"string","example":"dq-v002"},"dataset_assets":{"type":"string","description":"JSON-encoded array containing dataset asset and version information.","example":"[{\"asset_key\":\"trinkwasseranalyse\",\"deid\":\"https://cities.app/de/by/alfeld/water/drinking-water-quality\",\"schema\":\"https://cities.app/schema/water/drinking-water-quality\",\"supply_type\":\"supply-type-dataset\",\"versions\":[{\"event_id\":\"$U_1Gc8e1SGxjfQc0PE_HR5C4TGh20U3lheQZhWtV6Gc\",\"version_key\":\"2025\"}]}]"},"api_assets":{"type":"string","description":"JSON-encoded array containing API asset information.","example":"[]"},"id":{"type":"string","example":"!QHQceyRcuDHQMPqHUk:matrix.bytem.alpha.cities.app"},"room_id":{"type":"string","example":"!QHQceyRcuDHQMPqHUk:matrix.bytem.alpha.cities.app"},"room_name":{"type":"string","example":"Alfeld — Trinkwasserqualität"},"room_alias":{"type":"string","example":"#alfeld-—-trinkwasserqualität_supply_room.type:matrix.bytem.alpha.cities.app"},"numRecords":{"type":"integer","example":1}},"additionalProperties":false},"message":{"type":"string","example":"DEID found successfully"},"success":{"type":"boolean","example":true}},"additionalProperties":false},"OutgoingReference":{"type":"object","required":["target_deid","label","kind","role"],"properties":{"target_deid":{"type":"string"},"label":{"oneOf":[{"type":"string"},{"type":"null"}]},"kind":{"type":"string","enum":["demand","supply"]},"role":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":true},"IncomingReference":{"type":"object","required":["source_deid","label","kind","role"],"properties":{"source_deid":{"type":"string"},"label":{"oneOf":[{"type":"string"},{"type":"null"}]},"kind":{"type":"string","enum":["supply"]},"role":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":true},"ReferencesResponseData":{"type":"object","required":["source_deid","direction"],"properties":{"source_deid":{"type":"string"},"direction":{"type":"string","enum":["out","in","both"]},"references":{"type":"array","items":{"$ref":"#/components/schemas/OutgoingReference"}},"referenced_by":{"type":"array","items":{"$ref":"#/components/schemas/IncomingReference"}},"note":{"type":"string"}},"additionalProperties":true},"ReferencesApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"$ref":"#/components/schemas/ReferencesResponseData"},"message":{"type":"string","example":"References resolved successfully"},"success":{"type":"boolean"}},"additionalProperties":false},"DeidAccessRequest":{"type":"object","required":["deid","matrix_user"],"properties":{"deid":{"type":"string","format":"uri","description":"DEID for which access is requested.","example":"https://cities.app/de/rp/aach"},"matrix_user":{"type":"string","description":"Matrix user. Accepted forms include @user:server, user:server,\nand user@server. The value is normalized to @user:server.\n","example":"@test_bytem_matrix_org:matrix.org"},"email":{"type":"string","description":"Optional email address for the request.","example":"optional@example.com"},"location":{"oneOf":[{"$ref":"#/components/schemas/Location"},{"type":"null"}]},"idempotency_key":{"type":"string","maxLength":200,"description":"Optional request-body equivalent of the Idempotency-Key header.\n"}},"additionalProperties":false},"DeidAccessResponseData":{"type":"object","required":["accepted","request_id","status","matrix_user"],"properties":{"accepted":{"type":"boolean","example":true},"request_id":{"type":"string","format":"uuid","example":"a5eac8a3-04bb-4894-81b7-010e82b70727"},"status":{"type":"string","example":"queued"},"matrix_user":{"type":"string","pattern":"^@[^:]+:.+$","example":"@test_bytem_matrix_org:matrix.org"},"idempotent_replay":{"type":"boolean","example":false}},"additionalProperties":false},"DeidAccessApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"$ref":"#/components/schemas/DeidAccessResponseData"},"message":{"type":"string"},"success":{"type":"boolean"}},"additionalProperties":false},"DeidAccessStatus":{"type":"object","required":["status"],"properties":{"status":{"type":"string","example":"queued"},"room_id":{"type":"string","example":"!GQRInMUYmGvHsXUAwW:matrix.bytem.alpha.cities.app"},"indexed":{"type":"boolean","example":false},"reason":{"type":"string"},"failed_at_status":{"type":"string"}},"additionalProperties":false},"DeidAccessStatusApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"$ref":"#/components/schemas/DeidAccessStatus"},"message":{"type":"string","example":"Request status fetched successfully"},"success":{"type":"boolean"}},"additionalProperties":false},"AgentAccessResponseData":{"type":"object","required":["deid","room_id","guest","exchanged_event_count","exchanged"],"properties":{"deid":{"type":"string","example":"https://cities.app/de/he/eppstein"},"room_id":{"type":"string","example":"!GQRInMUYmGvHsXUAwW:matrix.bytem.alpha.cities.app"},"reused":{"type":"boolean","nullable":true,"example":true},"guest":{"type":"object","required":["matrix_id"],"properties":{"matrix_id":{"type":"string","example":"@4:matrix.bytem.alpha.cities.app"}},"additionalProperties":false},"exchanged_event_count":{"type":"integer","example":3},"exchanged":{"type":"array","description":"The delivered content, one entry per DEID this exchange covered. It\ncomes from the room's messages, not its demand descriptor state\nevents, which only name the event IDs to read. `events` is what\nPOST /api/room/stateEvents/exchange (fetchExchangeEvents) returned\nfor those IDs.\n","items":{"type":"object","properties":{"deid":{"type":"string"},"events":{"type":"array","items":{"type":"object"}},"error":{"type":"string"}}}}},"additionalProperties":false},"AgentAccessApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"$ref":"#/components/schemas/AgentAccessResponseData"},"message":{"type":"string"},"success":{"type":"boolean"}},"additionalProperties":false},"AgentAccessPendingResponseData":{"type":"object","required":["deid","request_id","status","retry_url","status_url"],"properties":{"deid":{"type":"string","example":"https://cities.app/de/he/eppstein"},"request_id":{"type":"string","format":"uuid"},"status":{"type":"string","example":"queued"},"retry_url":{"type":"string","format":"uri","description":"This route with request_id; fetch it until it answers 200."},"status_url":{"type":"string","format":"uri"},"note":{"type":"string","description":"What to do with retry_url, and the exact sentence to report to the user if the caller cannot follow it."}},"additionalProperties":false},"AgentAccessPendingApiResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"$ref":"#/components/schemas/AgentAccessPendingResponseData"},"message":{"type":"string"},"success":{"type":"boolean"}},"additionalProperties":false},"ErrorResponse":{"type":"object","required":["statusCode","data","message","success"],"properties":{"statusCode":{"type":"integer"},"data":{"type":"object","additionalProperties":true},"message":{"type":"string"},"success":{"type":"boolean","example":false}},"additionalProperties":false}}}}