Appearance
Push notifications
When the user is offline (the app is closed), an Intercom webhook tells the backend an operator replied — and the backend sends a push so the user comes back. When the app is open, polling already delivers everything, so no push is sent.
There are two channels, both optional and both no-op until configured:
| Channel | Platform | Transport |
|---|---|---|
| FCM | iOS + Android (React Native) | Firebase Cloud Messaging |
| Telegram | Telegram Mini App (TMA) | Telegram Bot API |
How it flows
Operator replies in Intercom
└─▶ Intercom webhook → POST /webhooks/intercom
└─▶ verify signature → 200 → (async) look up the user's devices + prefs
├─ device.platform = "mobile" → FCM.send(token, …)
└─ device.platform = "tma" → Telegram.sendMessage(chatId, …)The SDK registers the device for you: when you pass fcmToken (mobile) or telegramChatId (TMA) to <AWChat />, it calls POST /api/devices/register automatically. On logout, teardown() (web) / unmount (RN) unregisters it.
You never call the device API yourself — you only need to (1) configure the server, and (2) obtain the token/chat-id and pass it as a prop.
A) FCM — mobile (iOS + Android)
1. Server: add the Firebase service account
In the Firebase console, open (or create) the project for your app.
⚙︎ Project settings → Service accounts → Generate new private key. This downloads a JSON file (keep it secret — it grants send rights).
Put that JSON on the server (or mount it as a secret) and point the env var at its path:
bashFCM_SERVICE_ACCOUNT=/etc/aw-chat/firebase-sa.json
That's all the server needs. If the var is unset or the file is missing, FCM calls are skipped (logged, never crash).
2. App: get the device's FCM token
In the React Native app, use Firebase Messaging (e.g. @react-native-firebase/messaging): request notification permission, then read the token.
ts
import messaging from "@react-native-firebase/messaging";
await messaging().requestPermission(); // iOS prompt; Android auto on <13
const fcmToken = await messaging().getToken(); // the device tokeniOS also needs an APNs key uploaded in Firebase (Project settings → Cloud Messaging → Apple app configuration). Android works once
google-services.jsonis in the app. This is standard Firebase setup, outside the SDK.
3. App: pass it to <AWChat />
tsx
<AWChat
userId={user.id}
sessionToken={token}
serverUrl={SERVER}
telemetry={{ appVersion: "6.1.4", hardwareId: "iPhone15,2", osVersion: "iOS 17.4", deviceLang: "ru" }}
fcmToken={fcmToken} // ← registers this device for mobile push
/>The SDK registers it (platform: "mobile"). Refresh it when Firebase rotates the token (messaging().onTokenRefresh) by re-mounting with the new value.
4. Payload the device receives
json
{
"notification": { "title": "Поддержка", "body": "Новое сообщение от оператора" },
"data": { "type": "chat", "conversationId": "123456" }
}data.type is chat | ticket | system; conversationId or ticketId is set; csat: "true" is present when a resolved ticket should trigger the CSAT popup. Map these to initialRoute + targetId when the user taps the notification (see Deep linking).
B) Telegram — Mini App (TMA)
1. Create the bot and Mini App
- In Telegram, talk to @BotFather →
/newbot→ choose a name and username. BotFather returns the bot token — this isTELEGRAM_BOT_TOKEN. - Attach your Mini App URL to the bot (
/newappin BotFather, or Bot Settings → Configure Mini App). The bot's username (without@) isTELEGRAM_BOT_USERNAME— it's what builds the deep-link button.
2. Server: set both env vars
bash
TELEGRAM_BOT_TOKEN=7123456:AAF... # from BotFather
TELEGRAM_BOT_USERNAME=AWBot # the bot's username, no @Unset → Telegram pushes are skipped (logged, never crash).
3. App: pass the Telegram user id
Inside the Mini App (running @aw-chat/web), read the Telegram user id from the WebApp SDK and pass it as telegramChatId:
tsx
const tg = window.Telegram.WebApp;
<AWChat
userId={awUserId}
sessionToken={token}
serverUrl={SERVER}
telemetry={{ appVersion: "6.1.4", hardwareId: "tma", osVersion: "tma", deviceLang: tg.language_code }}
telegramChatId={String(tg.initDataUnsafe.user.id)} // ← registers TMA push
theme={tg.colorScheme}
/>The SDK registers it (platform: "tma", the chat id is the token).
The user must have pressed Start on the bot at least once. Telegram's Bot API can only message a
chat_idthat opened a conversation with the bot; otherwisesendMessagereturns403. Opening the Mini App through the bot normally satisfies this.
4. The message + deep link
The push is a normal bot message with an inline button:
Поддержка
Новое сообщение от оператора
[ Открыть чат ] → https://t.me/<TELEGRAM_BOT_USERNAME>/app?startapp=chat_123456The startapp payload is <type>_<id> (e.g. chat_123456, ticket_98765). When the Mini App launches, read it and route in:
ts
const param = tg.initDataUnsafe.start_param; // "chat_123456"
const [type, id] = param ? param.split("_") : [];
// → <AWChat initialRoute={type === "ticket" ? "ticket" : "chat"} targetId={id} skipWelcome />Notification preferences
Each user can mute a category. The SDK reads/writes them; the dispatcher checks them before sending, so a muted category delivers nothing.
| Method | Path | Body |
|---|---|---|
| GET | /api/settings/notifications | — (defaults: all on) |
| PUT | /api/settings/notifications | { chatMessages?, ticketUpdates?, systemNotices? } |
chatMessages→type: "chat"pushesticketUpdates→type: "ticket"pushessystemNotices→type: "system"pushes
One user, multiple devices
A user can have several registered devices (e.g. a phone + the Mini App). The dispatcher fans out to all of them; one device failing never blocks the others. Each registration upserts by (user, token), so re-passing the same token won't create duplicates.
Troubleshooting
| Symptom | Check |
|---|---|
| No push at all | Is the device registered? (passing fcmToken/telegramChatId triggers POST /api/devices/register.) Is the category enabled? Are the server env vars set? |
Logs say [fcm] skipped (not configured) | FCM_SERVICE_ACCOUNT is unset or the file path is wrong. |
Logs say [telegram] skipped (not configured) | TELEGRAM_BOT_TOKEN is unset. |
Telegram 403 | The user never pressed Start on the bot — Bot API can't open a chat. |
| Button missing on Telegram push | TELEGRAM_BOT_USERNAME is unset (no username → no deep link). |
| iOS gets nothing, Android works | APNs key not uploaded in Firebase, or missing push entitlement. |
Webhooks are the trigger for all of this — if pushes never fire, confirm the Intercom webhook is configured and verifying. See Webhooks.