Contextual Chat
The workflow for entering organization, cluster, or location chat contexts through PixService bootstrap.
Contextual chat uses PixService authorization to enter Matrix spaces for an organization, cluster, or location. Always start through a PixService bootstrap endpoint; an existing Matrix room membership is not proof of current PixService access.
Bootstrap a context
GET /v1/chat/orgs/{organizationId}/bootstrap
GET /v1/chat/clusters/{clusterId}/bootstrap
GET /v1/chat/locations/{locationId}/bootstrap
Authorization: Bearer <token>The current credential must pass one final, tier-capped decision:
- organization chat requires
IntegratedChatUse=AllowandOrganizationView=Allow; - cluster chat requires
IntegratedChatUse=AllowandClusterView=Readfrom the same principal; - location chat requires
IntegratedChatUse=Allow,ClusterView=Read, andLocationView=Readfrom the same principal.
Organization membership alone is not enough. The active entitlement version can disable chat without hiding clusters, locations, or bundles. Personal access tokens are also limited by their live organization scopes and capability ceilings. A denied request creates no chat context, room membership, or Matrix login token.
Missing and inaccessible contexts use the same opaque 404 Not Found response.
This applies to bootstrap, concrete channel-layout replacement, and organization
template reads or writes, so a denial does not reveal context names, channel
counts/layouts, or the capability that failed.
The successful response supplies the Matrix homeserver URL, Matrix user ID,
short-lived JWT login token, whether membership was ensured, and the current
space/channel identifiers. Use the login token with Matrix login type
org.matrix.login.jwt, then begin Matrix sync against the returned homeserver.
For a chat notification that carries a channel room ID, use
POST /v1/chat/rooms/resolve with { "matrixRoomId": "!channel:server" }.
It returns the currently authorized context and matching active channel
without requiring a map lookup or full chat directory. Unknown or revoked
destinations return the same 404. This lookup does not create Matrix rooms,
mark messages read, or check whether a targeted message still exists.
Discover conversations
The account catalog finds currently authorized conversations without location
IDs from the map or a Matrix history download. It defaults to locations and
returns up to 50 items. The maximum page size is 100.
During the staged rollout, a deployment that has not enabled the catalog
returns 503 chat_unavailable; continue using the existing contextual chat
bootstrap and location directory until the capability is available.
GET /v1/chat/conversations?type=location&limit=50
Authorization: Bearer <token>Use type=all, organization, or cluster for other context types. query
searches conversation and breadcrumb names without case sensitivity. Each item
has a stable conversationId, display name, breadcrumb, resource IDs, current
context and channel identifiers, provisioning state, and pin position. A
resource that is eligible for chat but has no Matrix context yet appears as
pending. A nullable unread count or activity timestamp is unavailable; do not
display it as zero. Once Matrix reconciliation is enabled, a user-session page
may report unreadNotificationCount, highlightCount, and
summaryFreshness: "fresh" for a context whose every channel has a recent
verified Matrix count. Missing, stale, or changed room mappings keep the
counts null. These are Matrix notification counts, not a count of all unread
messages. A scoped PAT receives no account-wide count state. A confirmed new
message can supply lastActivityEventId, lastActivityAtUtc,
lastActivitySenderId, and lastActivityKind (message or encrypted).
Message text is not stored in this catalog.
If hasMore is true, pass the opaque nextCursor with the same type, query,
and credential on the next request. A cursor is valid for 30 minutes and
is single use. A malformed or mismatched cursor returns
400 chat_catalog_cursor_invalid; a consumed, expired, or pin-revision-stale
cursor returns 409 chat_catalog_reset_required. Restart at page one after a
reset and merge entries by conversationId. Access is rechecked on each page.
Matching pins lead in stored order and count toward the page limit. Existing chats without a catalog-observed visible message start in stable name and ID order. A later message moves an unpinned chat up after Matrix sync confirms that user can see it. Reading, edits, redactions, and delayed pushes do not change activity order. Initial historical messages do not establish order.
GET /v1/chat/summary reads every currently authorized conversation for a
user session, including pages the client has not opened. It reports
authorizedConversationCount, pendingConversationCount, changeSequence,
snapshotAtUtc, and freshness. When every active room has a recent Matrix
count, unreadConversationCount, unreadNotificationCount, and
highlightCount contain account totals. If any room is missing or stale, all
three totals are null. unreadNotificationCount counts Matrix notifications,
not every unread message. Scoped PATs cannot read this account summary.
Save chat pins across devices
Pinned conversations are account preferences. They do not grant access, join a
Matrix room, change unread state, or mute notifications. Pin preference routes
require a user session; personal access tokens cannot read or replace the
account's pin set. A pin references a stable conversation ID such as
location-5f920a386ce54d1f91b309c37761de28,
cluster-5f920a386ce54d1f91b309c37761de28, or
org-5f920a386ce54d1f91b309c37761de28.
GET /v1/chat/pins
Authorization: Bearer <user-session-token>The response contains revision and ordered pins with conversationId and
zero-based position. It also returns a quoted revision in ETag, for example
"3". Only currently accessible pins appear in the response.
PUT /v1/chat/conversations/{conversationId}/pin
DELETE /v1/chat/conversations/{conversationId}/pin
PUT /v1/chat/pins/order
Authorization: Bearer <user-session-token>
If-Match: "3"Pinning appends after existing pins; repeating an existing pin leaves its
position and revision unchanged. At most five distinct conversations can be
pinned. Reordering takes { "conversationIds": ["location-...", "org-..."] }
with exactly the current pins once each; it cannot add or remove pins. An
uncertain successful write can be retried: if the desired state is already
current, the server returns it without another revision change. A stale change
returns 412 chat_pin_revision_conflict; refresh GET /v1/chat/pins and retry.
A sixth distinct pin returns 409 chat_pin_limit_reached. Missing If-Match
returns 428 chat_pin_revision_required. Unknown or inaccessible conversation
IDs remain opaque with 404.
Each real pin change writes a durable, revisioned account invalidation. A
connected user-session client may receive ChatAccountChanged on the existing
notification hub with { "kind": "pins_changed", "pinRevision": 4 }. Treat it
as a hint and fetch authoritative state. To catch up after reconnect:
GET /v1/chat/conversations/changes?since={changeSequence}&limit=100
Authorization: Bearer <user-session-token>Process items in sequence, follow nextSequence while hasMore is true,
and persist the sequence only after handling that page. Each item currently
contains sequence, kind, pinRevision, and createdAtUtc. A
409 chat_changes_reset_required response means the cursor is outside the
retained journal; discard cached chat-list state and restart from a fresh
snapshot. Use the account catalog page's changeSequence as the snapshot watermark.
Change channel layouts
Chat access does not grant layout-management access. Full channel replacement uses these additional final capabilities:
- organization layouts and cluster/location templates:
OrganizationSettingsWrite=Allow; - one concrete cluster layout:
ClusterUpdate=Allow; - one concrete location layout:
LocationUpdate=Allow.
Channels omitted from a replacement request are archived or removed according to that endpoint's contract. Re-read bootstrap after a successful change to refresh room identifiers and provisioning state.
Handle permission changes
Treat PixService as the authority. Do not cache a Matrix invite, joined-room list, or earlier bootstrap response as an authorization decision. A permission revocation, entitlement transition, suspension, or expiry can remove desired Matrix membership while spatial and bundle access remains otherwise unchanged. Re-run bootstrap when navigation context or credentials change, and handle a denied or not-found response without attempting direct room access through the PixService client flow.