Push notifications
MarkdownEvery team or AI reply is pushed to each of the customer’s devices that has a push token. Concierge never asks for notification permission; you do that, when it makes sense in your app, and forward the token.
Upload your credentials
Section titled “Upload your credentials”In the dashboard, open your app’s Push settings:
- iOS: your APNs auth key (
.p8), key ID and team ID. Concierge picks the sandbox or production gateway per device automatically. - Android: a Firebase service account JSON for FCM HTTP v1.
Without credentials, local dev logs each push instead of sending it, so you can build without them.
func application(_ app: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken token: Data) { Concierge.registerPushToken(token) // sandbox or production is detected for you}
// UNUserNotificationCenterDelegate on a @MainActor class. Don't mark these nonisolated.func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { let userInfo = notification.request.content.userInfo if Concierge.handleNotification(userInfo, openChat: false) { return Concierge.shared.isChatVisible ? [] : [.banner, .sound] } return [.banner, .sound]}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { Concierge.handleNotification(response.notification.request.content.userInfo) // opens the chat}Android
Section titled “Android”Concierge doesn’t depend on Firebase. Forward from your own FirebaseMessagingService:
override fun onNewToken(token: String) = Concierge.registerPushToken(token)
override fun onMessageReceived(message: RemoteMessage) { if (Concierge.handleNotification(message.data)) { // Ours: show a notification that calls Concierge.open(context) when tapped. return } // Yours.}handleNotification returns true for Concierge payloads and updates the unread count.
Payload
Section titled “Payload”Every push carries the same data fields:
| Field | Value |
|---|---|
source |
"concierge" |
conversationId |
"cnv_..." |
messageId |
"msg_..." |
unread |
Messages this customer hasn’t read yet |
APNs. The alert lives in aps: the title is the team member’s name when Show team names is on, otherwise your app’s name; the body is the message preview; badge is the unread count; thread-id is the conversation id. Data fields sit in a top-level concierge dictionary:
{ "aps": { "alert": { "title": "Kirill", "body": "Fixed in 2.4, update when you can" }, "badge": 1, "thread-id": "cnv_..." }, "concierge": { "source": "concierge", "conversationId": "cnv_...", "messageId": "msg_...", "unread": 1 }}FCM. A notification block (title, body) plus a data map where every value is a string:
{ "source": "concierge", "conversationId": "cnv_...", "messageId": "msg_...", "unread": "1", "title": "Kirill", "body": "Fixed in 2.4, update when you can" }Tokens the gateway reports as invalid are cleared from the device automatically.