Connect Building Chats with Matrix

Prepare building chats asynchronously and share one Matrix sync stream per account.

Prepare on app entry

Every accessible organization-owned building has a conversation. Users do not create or accept it manually. New buildings receive local chat metadata at creation; existing buildings are prepared in bounded batches when the client loads its catalog.

  1. Read POST /v1/chat/locations/directory with { "locationIds": ["building-uuid"] }. Submit 1–100 IDs per request, including shared buildings. authorizedLocationIds identifies effective access independently of contexts. Pending contexts may have null Matrix room IDs. This lookup never provisions or joins rooms.
  2. Obtain GET /v1/chat/session using an active user session. The response contains homeserverBaseUrl, serverName, matrixUserId, and a short-lived loginToken. Scoped PATs cannot use this endpoint. It does not wait for membership provisioning.
  3. Log in to /_matrix/client/v3/login with type: "org.matrix.login.jwt" and token: loginToken. Keep the resulting Matrix access token in secure device storage.
  4. Submit the authorized IDs to POST /v1/chat/locations/prepare, in batches of at most 100. HTTP202 returns accepted locationIds; it does not assert that membership is ready. Preparation coalesces background work. Refresh the directory while contexts are pending.
  5. Run one Matrix /sync stream for all returned channel room IDs. A room appearing under rooms.join establishes readiness. Opening a conversation observes this shared stream and local history; it does not call bootstrap again.

Access and participants

A chat push can name a Matrix room before the app has loaded a map pin. Resolve that destination directly:

POST /v1/chat/rooms/resolve
Authorization: Bearer <token>
Content-Type: application/json

{"matrixRoomId":"!channel:server"}

The response contains context (the current authorized workspace and active channels), channelId, and matrixRoomId. It does not include message text or unread state. Unknown, archived, deleted, and inaccessible rooms return the same 404; malformed or overlong IDs return 400. This read-only request does not create or join Matrix rooms. A scoped PAT can resolve only contexts allowed by its current ceiling; it cannot use the Matrix session or bootstrap route. After resolution, load the selected room in Matrix. If the pushed event is no longer available, open the room without an event target.

Use GET /v1/chat/locations/{locationId}/participants?offset=0&limit=100 for current authorized accounts. The response contains items and totalCount. Each item provides userId, matrixUserId, displayName, and optional signed avatarUrl. Matrix identity can be empty until its binding exists. This list represents access, not online presence. Continue pages until totalCount is reached and refresh after access changes; signed avatar URLs expire.

The participant endpoint and membership reconciliation share the final policy resolver. Shared-cluster guests are included when their effective cluster and location visibility permits chat. Spatial visibility supplies the chat default for the same principal; explicit chat denial, entitlement ceilings, and credential restrictions still apply. Matrix membership itself is never proof of current Pix access.

After access is revoked, stop affected room operations and remove its local messages, drafts, queued attachments, and media. Refresh authorization on app entry and periodically while active. Reconciliation removes stale Matrix membership in the background.

Local history and activity

Store messages, drafts, pending transactions, read markers, and history cursors under the current account and organization. Persist room changes before advancing the shared sync token. Use stable Matrix transaction IDs when retrying sends. Download older history in the background and continue across empty pages while an end cursor exists. Load member state lazily; use the participant endpoint for the access roster.

Show all buildings. Conversations with messages sort by newest message across their channels; empty conversations follow by most recent local opening, then natural building/cluster name. A message from any participant moves its building up. Keep cached content usable during reconnects and distinguish pending first load from an empty conversation.

Images in the document explorer

Deployments can enable automatic archival of new image messages from managed building chats. The backend copies the original Matrix image into the building's Chat document folder asynchronously; clients must not upload a second copy through the document API. Normal document listing and thumbnail endpoints expose the result.

Every currently authorized chat member can contribute through this bridge, including shared-cluster guests without ordinary document-write permission. This grants no additional permission to read, edit or delete documents elsewhere. Document explorer access still follows document permissions.

Archived images preserve the bytes and EXIF/GPS metadata present in Matrix. Supported originals are JPEG, PNG, HEIC, WebP and GIF up to 20 MiB. Encrypted media, remote-homeserver media, message edits and historical backfill are outside this integration. An image may appear after a delay while the backend retries or waits for storage capacity; a sent chat message alone does not prove archival.

Removing a Matrix message leaves its archived document intact. Documents are deleted independently in the explorer. Repeated event delivery does not create duplicates or recreate a document already deleted there.

Retries and compatibility

Honor the full Retry-After or Matrix retry_after_ms deadline; do not shorten long delays. A legacy bootstrap can return HTTP503 with Retry-After: 10 while another writer updates the same Matrix membership; retry that bootstrap with the current user session. Provisioning retries are persisted in the existing background-task JSON payload. They require no schema migration. The backend handles invitation and joining independently of foreground reads.

Existing organization, cluster, and location bootstrap endpoints remain available for older user-session clients. Scoped PATs receive HTTP404 before bootstrap side effects because a Matrix account token cannot enforce a PAT scope. New building-chat clients use session, directory, and asynchronous preparation instead of repeating bootstrap during navigation.

Push registration remains a separate Matrix pusher operation. Failures must not block timeline sync. See the deployed push-gateway contract for the application ID and gateway URL; never derive them from an untrusted message.

On this page