Notifications & Events
How the Stay22 notification lifecycle works, how to customize the copy, and how to observe every decision the SDK makes.
The notification lifecycle
Once you set a travel context, the notification moves through a simple lifecycle — and the SDK tells you about every step through events:
- Scheduled. The SDK accepts the trip and queues one notification for it. A newer travel context replaces the pending one.
- Delivered. After the user leaves your app and the Stay22-managed delay elapses, the notification is shown.
- Opened. The user taps it and lands in a curated booking experience for their destination and dates, attributed to your
aid. - Skipped or cancelled. If conditions aren't right — the user is still in the app, the destination is their home city, a cooldown is active, permission is missing — the SDK holds back and emits the reason instead.
The delivery delay and frequency caps are configured by Stay22 for your account, not in code — that keeps timing tuned without app releases. Contact your Stay22 account manager or support@stay22.com to adjust them.
Customize the notification
The default copy works out of the box, but you can match it to your app's voice with notificationConfig. Set it once, any time after initialization:
import com.stay22.sdk.NotificationConfig
import com.stay22.sdk.NotificationInterruptionLevel
Stay22.notificationConfig = NotificationConfig(
title = "Hotels in {destination}",
subtitle = "{checkin} - {checkout}",
message = "Tap to find the best deals nearby",
categoryId = "stay22_destinations",
threadId = "stay22",
interruptionLevel = NotificationInterruptionLevel.active
)Stay22.notificationConfig = NotificationConfig(
title: "Hotels in {destination}",
subtitle: "{checkin}-{checkout}",
message: "Find stays near {destination}.",
categoryId: "stay22_destinations",
threadId: "stay22",
badge: 1,
interruptionLevel: .active,
relevanceScore: 0.8
)iOS additionally supports launchImageName, targetContentIdentifier, image attachments (local files), and custom actions buttons — see the iOS README for the full shape.
Text placeholders
Title, subtitle, and message support placeholders that the SDK fills in from the travel context:
| Placeholder | Replaced with |
|---|---|
{destination} | The trip's destination name |
{checkin} | Check-in date, when provided |
{checkout} | Check-out date, when provided |
Attribution
Bookings are attributed to the aid you initialized with. To segment where conversions come from — much like campaign on Allez links — set an optional campaign ID:
Stay22.campaignId = "spring_push"Stay22.campaignId = "spring_push"Events
The SDK emits an event for every decision it makes, which is the best window into what it's doing — wire them into your analytics or logging:
import com.stay22.sdk.Stay22Event
Stay22.advanced.setEventHandler { event ->
when (event) {
is Stay22Event.NotificationScheduled -> log("scheduled: ${event.destination}")
is Stay22Event.NotificationClicked -> log("clicked: ${event.destination}")
is Stay22Event.NotificationSkipped -> log("skipped: ${event.reason}")
else -> Unit
}
}Stay22.advanced.setEventHandler { event in
switch event {
case .notificationScheduled(let destination, let delay):
print("scheduled: \(destination), delay=\(delay)s")
case .notificationClicked(let destination, let url):
print("clicked: \(destination), \(url)")
case .notificationSkipped(let reason):
print("skipped: \(reason)")
default:
break
}
}| Event | Fires when |
|---|---|
locationUpdated | A travel context was accepted |
notificationScheduled | A notification was queued (destination, delay) |
notificationShown | The notification was delivered |
notificationClicked | The user tapped it (destination, booking URL) |
notificationSkipped | Scheduling was skipped (reason) |
notificationBlocked | Delivery was blocked (reason) |
notificationCancelled | A pending notification was cancelled (reason) |
enabledChanged | Stay22.isEnabled was toggled |
travelContextCleared | The travel context was cleared |
Skip and block reasons
When the SDK holds back, the reason tells you why — most are expected behavior, not errors:
| Reason | Meaning |
|---|---|
sdk_disabled | Stay22.isEnabled is false |
missing_destination | The travel context has no usable destination |
app_foreground | The user was still in your app at delivery time |
home_destination | The destination looks like the user's home area |
partner_config_disabled | Scheduling is currently disabled for your account |
local_cooldown | A notification was shown too recently (frequency cap) |
duplicate_pending_notification | A notification for this trip is already pending |
notification_permission_denied | The user hasn't granted notification permission |
Manual scheduling
By default the SDK schedules automatically when a travel context is set. If you want to control the trigger yourself — for example, only after checkout of a flight purchase — run the same pipeline manually:
Stay22.advanced.scheduleNotification()Stay22.advanced.scheduleNotification()All the same relevance checks still apply — manual scheduling triggers the pipeline, it doesn't bypass it.