An embed can schedule a notification on the user's device and can ask the host application for notification consent. Calling requestConsent() also lets the mobile app prepare the device for remote push notifications when your service has an MBP backend notification integration.
The SDK does not send remote pushes. It does not expose a device token either. Your backend continues to send notifications through its agreed MBP backend integration, the Notificaties API.
| Scheduled notification | Remote push notification | |
|---|---|---|
| Starts from | Your embed frontend | Your service backend |
| Timing | Now or at a chosen date | When your backend sends an event |
| SDK method | scheduleNotification() | No send method in the SDK |
| Consent path | May ask for consent while scheduling | Call requestConsent() from an explicit user action |
| Needs the user to be signed in to the app | No | Yes, when consent is requested |
Use a scheduled notification for a local reminder. For example, remind someone about an appointment at a date they chose in the embed.
Use remote push when your backend needs to notify the user later. For example, when a submitted application changes. A successful requestConsent() makes the device ready for that delivery. It does not make your backend send anything.
Do not schedule a local notification just to ask for push consent. Call requestConsent() from a clear "Turn on notifications" action instead.
Read the permission state when the embed opens and whenever it returns from a host application screen:
const permissions = await mbpClient.notifications.getNotificationPermissions();
if (permissions.granted) {
showNotificationsEnabled();
} else {
showNotificationsDisabled();
}
For normal embed flows, use these two values:
const { granted, canAskAgain } = permissions;
granted is the value that matters to an embed. It is true only when both of these are true:
canAskAgain says whether requestConsent() can currently show a useful consent flow. The host
application returns false when system settings block another prompt, a prompt is already open,
or the embed asked too recently. It intentionally does not say which reason applies.
Treat these states as follows:
granted |
canAskAgain |
What to do |
|---|---|---|
true |
either value | Notifications are available. |
false |
true |
Show an enable action when it makes sense for the user. |
false |
false |
Do not keep asking. Leave notifications off and, if useful, point to the app notification settings. |
The host application can allow notifications at operating-system level while they remain disabled for this embed.
Call requestConsent() only after a user action. A settings toggle or a button labelled "Turn on notifications" are good places. Do not call it when the page loads, during rendering, after a route change, or from a retry loop.
async function enableNotifications() {
const permissions = await mbpClient.notifications.requestConsent();
if (permissions.granted) {
showNotificationsEnabled();
return;
}
showNotificationsDisabled();
if (!permissions.canAskAgain) {
showNotificationSettingsLink();
}
}
The host application may show one or more screens during this call:
The promise resolves after that flow finishes, then returns a fresh permission snapshot. A user
declining a prompt is a normal result. Check granted; do not treat a declined prompt as an
error.
The host application limits repeated consent prompts. When it cannot prompt again, requestConsent() resolves with the unchanged permission state. It does not repeatedly display a dialog. The exact cooldown can change, so do not show a countdown based on it.
A successful call lets the host application prepare the device for remote delivery. It does not subscribe the user to a provider-side preference list. Keep any service-specific notification preferences in your own product and explain them separately.
Expose a deliberate "Turn off notifications" action if your embed has an in-embed notification setting:
async function disableNotifications() {
const permissions = await mbpClient.notifications.revokeConsent();
showNotificationsDisabled();
}
revokeConsent() turns off this embed's consent and removes its request for remote delivery. It also cancels local notifications scheduled by the embed. It does not change the operating system's app-level notification permission.
Do not call revokeConsent() when a component unmounts, when a user signs out of your service, or as part of a failed request. It is a user choice, not cleanup.
revokeConsent() only manages consent in the host application. If your service also keeps its own notification preferences, update those separately.
When notifications are blocked and canAskAgain is false, offer a link to the app's
notification settings. The user can review system permission and embed settings there.
import { AppHostRoutes } from '@govflanders/mbp-embed-sdk';
await mbpClient.navigation.openHostRoute(AppHostRoutes.Notificaties);
Read the permission state again when the user returns. Opening an app route covers the embed,
but the page stays alive in the mobile app. The browser visibilitychange event is a reliable
return signal:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
void refreshNotificationPermission();
}
});
async function refreshNotificationPermission() {
const permissions =
await mbpClient.notifications.getNotificationPermissions();
updateNotificationUi(permissions);
}
Only refresh state from this handler. Never call requestConsent() from visibilitychange.
The user did not ask for a prompt by returning to the embed.
The host application's notification screen can link to a settings URL configured for your embed. That page opens as your embed and is the right place for choices the host application cannot manage on your behalf, such as notification topics, individual subscriptions, or provider-side quiet hours.
Ask the Mijn Burgerprofiel team to configure the URL as part of onboarding. The host application uses the embed label for the link. A user can then open the page from the host application's notification settings.
Keep two levels of control separate on that page:
For example, a page might offer a general "Notifications on" switch backed by requestConsent() and revokeConsent(), followed by topic switches such as "Appointment reminders" or "Application updates" backed by your service. When general consent is off, keep the topic selections visible if that helps the user, but make clear that the service cannot deliver remote notifications until they turn general consent back on.
The detail page can use the same notification SDK methods as the rest of your embed. Refresh permissions when it becomes visible again, as shown above. This matters when the user moves between the detail page and the host application's settings.
scheduleNotification() creates a notification on the device. It returns its identifier when
scheduling succeeds, or undefined when notifications are unavailable or the user does not
give consent.
const identifier = await mbpClient.notifications.scheduleNotification({
content: {
subtitle: 'Appointment reminder',
body: 'Your appointment starts in one hour.',
priority: 'high',
data: {
appointmentId: 'a1b2c3',
},
},
trigger: {
date: new Date('2026-06-18T09:00:00+02:00'),
},
action: {
type: 'embed',
url: '/appointments/a1b2c3',
},
});
if (!identifier) {
showReminderWasNotScheduled();
return;
}
saveScheduledReminder(identifier);
The host application supplies the notification title. Your embed supplies subtitle, body,
optional priority, and data. Do not put secrets or personal data in data; a notification
can appear on a locked device.
Omit trigger to show the notification immediately. Pass a Date for a one-time scheduled
notification. The SDK serialises dates across the embed boundary, so always construct the date
in your own timezone-aware application code.
A schedule request may start the local consent flow if consent is absent. It does not request remote-push registration. That difference is deliberate: a user asking for a reminder has not necessarily agreed to receive backend-driven notifications.
An embed action opens an embed URL. Relative URLs resolve against the current embed URL.
Use one for a page within your own embed:
action: {
type: 'embed',
url: '/appointments/a1b2c3',
}
A hostRoute action opens a route in the host application. Use it only for routes that make
sense outside your embed:
import { AppHostRoutes } from '@govflanders/mbp-embed-sdk';
action: {
type: 'hostRoute',
route: AppHostRoutes.Notificaties,
}
If you omit action but put a string url in content.data, the host application treats that
URL as an embed action. Prefer the explicit action field in new code.
getAllNotifications() returns the local notifications scheduled by the current embed. Keep the identifier if the user needs to cancel a specific reminder:
const notifications = await mbpClient.notifications.getAllNotifications();
await mbpClient.notifications.cancelNotification(identifier);
Cancel a reminder when the event it refers to is cancelled or its time changes. Schedule a new one after changing a date; do not assume an existing notification updates itself.
A simple notification preference screen normally needs these pieces:
AppHostRoutes.Notificaties instead of showing the same
prompt again.Local reminders fit alongside that screen, but do not make a local reminder the only way a user can enable remote notifications. The two actions have different consequences.
scheduleNotification(), requestConsent(), and revokeConsent() can reject when the SDK cannot communicate with the host application. This can happen when the embed is not configured to use notifications or when the host application does not support them. Catch those failures and show a normal technical-error state with a retry action.
A returned permission result with granted: false, or an undefined result from
scheduleNotification(), is not a communication error. It means the notification could not be
scheduled under the current consent and system-permission state.
Connect the SDK client before using notifications, as with every other SDK feature:
await mbpClient.connect();