# Messaging and rooms

> Reading room context, sending as the user, and the encryption constraint that decides your notification design.

Source: https://miniapp.sollar.com/guides/messaging-and-rooms/

---

Sollar is a messenger first. A mini app can be launched from inside a room, can know which room, and
can send a message on the user's behalf. What it cannot do is post from a server into an encrypted
room — and that constraint should shape your design from the first sketch.

## Room context

```js
const room = await sollar.room.current()        // requires room.context

if (room === null) {
  // Launched from the home grid, not from a room. This is the common case.
  renderStandalone()
} else {
  renderForRoom(room)
}
```

```ts
interface Room {
  room_id: string
  name?: string
  is_encrypted: boolean
  member_count: number
}
```

`null` is normal, not an error. Most launches come from the home grid.

**Check `is_encrypted` early** — it decides what your backend can do, and the answer should shape the
UI, not surface as a failure later.

## Sending as the user

```js
await sollar.message.send({
  room_id: room.room_id,
  body: 'Purchase order 4471 approved — €12,400 to Nordwerk GmbH.',
})                                              // requires room.send
```

The message leaves from **the user's own client**, under **the user's identity**, and the user
confirms each send. A mini app cannot post silently under someone's name, and there is no scope that
enables it.

Rate limit: 10 per minute, each confirmed.

### Cards

```js
await sollar.message.send({
  room_id: room.room_id,
  body: 'Purchase order 4471 approved',        // plain-text fallback
  card: {
    title: 'Purchase order 4471 approved',
    body: '€12,400 · Vendor: Nordwerk GmbH',
    actions: [{ label: 'Open', deep_link: '/orders/4471' }],
  },
})
```

`body` is required even when you send a card. It is what appears in notifications, in search, in
accessibility tooling, and in any client that cannot render your card. A card with a `body` of
"Update" is a card nobody can find again.

`deep_link` resolves against the routes in your manifest's `pages`. A link to an undeclared route
does not open.

## The encryption constraint

> **CAUTION**
**Your backend cannot post into an end-to-end encrypted room.**

Sollar's servers do not hold the room keys, and your backend is not a cryptographic member of the
room. That is what makes the encryption real, so there is no server-side path around it — not a
scope, not an enterprise tier, not a support ticket.

In the Enterprise and Sovereign tiers, encryption is **on by default**. Assume encrypted.

Two paths that work:

**1. Send from the user's client.**

```js
// Runs in the mini app, in the user's session, with the user's keys.
await sollar.message.send({ room_id, body })
```

Works in any room the user is in, encrypted or not. Requires the mini app to be open, and requires
the user to confirm.

**2. An agent that is a member of the room.**

An AI agent admitted to a room as a verified, cross-signed device can post under its own identity.
It sees the room's content exactly as an invited human member would — visible and auditable, not
confidential during processing. Your mini app's `actions[]` are what the agent calls; the agent
posts the result.

### Designing around it

The pattern that does **not** work:

```
order approved on your server  →  your server posts to the room  →  ✗ encrypted room
```

The patterns that do:

```
order approved  →  push notification to the user  →  user opens the mini app
                →  mini app posts with sollar.message.send()
```

```
order approved  →  your server tells the agent  →  the agent, a room member, posts
```

```
order approved  →  your server posts to a NON-encrypted room  →  works
```

The third is real: not every room is encrypted, and a tenant may designate an unencrypted channel
for system notifications precisely so that integrations can reach it. That is a tenant decision, not
one your mini app can make.

**Decide this before you build.** Discovering during integration that the notification feature is
impossible as designed is the expensive way to learn it.

## Notifications instead

When you cannot post to the room, notify the user directly:

```http
POST /v1/users/{app_user_id}/notifications
```

Requires the `notifications` scope from that user. It reaches the person rather than the channel —
which is often what was actually wanted, and is what makes the encrypted-room limit less painful
than it first sounds.

See [Server API](/reference/api/server/).

## Deep links

Every route in `pages` is deep-linkable:

```
https://sollar.com/app/com.acme.erp/orders/4471
```

Opens Sollar, launches the mini app, and routes to `/orders/4471`. Parameters arrive at
`sollar.app.launchParams`.

Public universal links for store-channel mini apps also satisfy Apple's Guideline 4.7.4, which
requires an index of the software available in the app, with universal links to all of it. That
index is [the public catalogue](/apps/).

## What a mini app cannot do

| | Why |
|---|---|
| Read a room's message history | No scope grants it. A mini app is not a client. |
| Post without the user confirming | Structural: sends are per-message confirmed. |
| Post as another user or as the system | The message carries the user's identity. |
| Create, join or leave a room | Room membership is the messenger's job. |
| Message a user who has not installed the app | No installation, no channel. |
| Navigate to another mini app | Cross-promotion, ranking and inter-app navigation are prohibited. |

The last row is a store policy borrowed verbatim from WeChat's rejection criteria, and the reason is
sound: a store where mini apps promote each other becomes an attention marketplace rather than a
tool directory.
