react-native-mrd-sdk
v2.0.4
Published
Tenant-scoped admin-panel API wrapper SDK for React Native push registration
Downloads
768
Readme
react-native-mrd-sdk
react-native-mrd-sdk is a React Native SDK for MRD tenant push registration.
It is a small, typed wrapper around the MRD admin-panel public push APIs.
The host app owns Firebase, permission prompts, token refresh listeners, and notification handling. The SDK owns the MRD API contract around those values: signed requests, typed helper methods, response validation, device metadata, identity helpers, tag helpers, retries, and React readiness state.
Table Of Contents
- What This SDK Does
- Step-By-Step Client Installation Guide
- API Reference
- Types And Accepted Values
- Signed Request Contract
- Error Handling
- Offline Retry Queue
- Push Payload Helpers
- Troubleshooting
- Verification
What This SDK Does
The SDK is designed around an appId from the MRD admin panel.
It handles:
- signed tenant public push app lookup by
appId - signed identity upsert by
appId - signed push subscription registration by
appId - signed token-state update by URL-encoded token
- signed user readback by
externalId - signed tag merge, replace, and delete by
externalId - request timestamp, nonce, body hash, HMAC signature, request id, and SDK version
- best-effort app/device metadata from
react-native-device-info - current-device token orchestration through a client-provided
pushTokenProvider - current-device permission status resolution for Android, with optional custom provider support
- identity lifecycle helpers such as
login,logout, andgetCurrentIdentity - local tag mutation batching before an identity is known
- optional offline sync retry queue
- React provider, hooks, and readiness boundary
- push payload normalization helpers for app-side open handling
It does not handle:
- Firebase installation or native Firebase configuration
- Firebase service-account handling or server-side push sending
- Firebase token acquisition unless the app provides
pushTokenProvider - notification permission UX owned by the app
- notification received/opened listeners
- anonymous token-only registration without
externalId,email, orphone
Step-By-Step Client Installation Guide
Follow these steps in order for a normal React Native Firebase Messaging integration.
Step 1: Confirm What The App Must Own
Before installing the SDK, make sure the client app team understands this split:
| Owner | Responsibility | | --- | --- | | Client app | Firebase installation, native app setup, notification permission UX, FCM token retrieval, token refresh listeners, foreground/background/opened notification handling. | | MRD SDK | Signed MRD API calls, request headers, HMAC signature, request id, SDK version, normalized responses, identity helpers, tag helpers, device metadata, retry behavior, React readiness state. |
The SDK does not configure Firebase and does not install notification listeners. It receives values from the app and sends them to the MRD tenant public push APIs safely.
Step 2: Install The SDK
Install the SDK:
yarn add react-native-mrd-sdkStep 3: Install Firebase Messaging In The Host App
If your app uses Firebase Cloud Messaging, install React Native Firebase:
yarn add @react-native-firebase/app @react-native-firebase/messagingThen complete the native Firebase setup for your iOS and Android apps:
- React Native Firebase: https://rnfirebase.io/
- React Native Firebase Messaging: https://rnfirebase.io/messaging/usage
This usually includes Firebase config files such as GoogleService-Info.plist
for iOS and google-services.json for Android, plus the native Gradle/CocoaPods
setup required by your app.
Step 4: Install Device Metadata Support
Install react-native-device-info directly in the host app. The SDK uses it for
best-effort app/device metadata, and a direct app dependency makes native
autolinking more reliable:
yarn add react-native-device-infoLibrary reference: https://github.com/react-native-device-info/react-native-device-info
Step 5: Run Native Install Steps
For iOS, run CocoaPods after adding native dependencies:
cd ios
pod install
cd ..Android 13 and newer require the POST_NOTIFICATIONS runtime permission for
notifications. The SDK can check/request that permission during current-device
registration, but your app should still own the user-facing permission timing
and explanation.
Step 6: Collect Required MRD Admin Values
Get these values from the MRD admin panel or your tenant app configuration:
| Name | Required | Description |
| --- | --- | --- |
| apiBaseUrl | Yes | Tenant API origin. It may include /api or omit it. |
| appId | Yes | Public push app id from the admin panel. |
| sdkAuthToken | Yes | Static SDK signing token from the admin panel. |
Example:
const apiBaseUrl = 'https://foo.localhost:5173';
const appId = 'mrd_htajyzzysucj41h6omw9wvci';
const sdkAuthToken = 'mrd_sdk_REPLACE_WITH_ADMIN_PANEL_TOKEN';Keep sdkAuthToken out of source control. Load it from your app environment,
remote config, or another secure configuration path appropriate for your app.
Step 7: Decide Required And Optional Runtime Values
Registration requires a push token, platform, and at least one user identity.
You can pass token/platform per call, or configure pushTokenProvider once and
use the current-device helpers.
| Name | Required | Description |
| --- | --- | --- |
| token | Yes for explicit-token methods | FCM/APNs-backed device token. |
| platform | Yes for explicit-token methods | One of ios, android, or web. |
| pushTokenProvider | Yes for current-device methods unless token is passed | App-provided function that returns the current device token. |
| externalId, email, or phone | Yes for identity, sync, and registration | At least one identity is required. Prefer stable externalId when available. |
These values are optional and can be passed only when your app has them:
| Name | Description |
| --- | --- |
| firstName, lastName | Profile fields sent during identity or registration calls. |
| attributes | Custom user attributes as an object. |
| tags | User tags as an object, or an array for backend-compatible registration payloads. |
| status | Subscription status: subscribed, unsubscribed, pending, or invalid. |
| osPermission | OS permission: granted, denied, unknown, or provisional. |
| backendCode | App/backend diagnostic code for registration or token state updates. |
| reason | Human-readable reason for token state updates. |
| meta | Additional subscription metadata. Merged with SDK-generated device metadata. |
| requestId | Override generated request id when you need your own correlation id. |
| sdkVersion | Override generated SDK version in registration payloads. |
| appId | Override the default client appId for a specific method call. |
Step 8: Create One Reusable SDK Client
Create a single client and reuse it for the app lifetime. The recommended setup
is to configure pushTokenProvider once, then use current-device helpers.
// mrdSdk.ts
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
import { createMrdSdkClient } from 'react-native-mrd-sdk';
const apiBaseUrl = 'https://foo.localhost:5173';
const appId = 'mrd_htajyzzysucj41h6omw9wvci';
const sdkAuthToken = 'mrd_sdk_REPLACE_WITH_ADMIN_PANEL_TOKEN';
export const mrd = createMrdSdkClient({
apiBaseUrl,
appId,
sdkAuthToken,
pushTokenProvider: async ({ forceRefresh }) => {
const token = await messaging().getToken();
return {
isNewToken: forceRefresh,
platform: Platform.OS === 'ios' ? 'ios' : 'android',
provider: 'firebase',
token,
};
},
deviceMeta: {
build_channel: 'production',
},
});createMrdSdkClient accepts these options:
| Option | Required | Description |
| --- | --- | --- |
| apiBaseUrl | Usually yes | Base tenant API URL. Required before network calls unless supplied by runtimeConfig. |
| appId | Usually yes | Default app id used by methods when no per-call appId is passed. |
| sdkAuthToken | Usually yes | SDK signing token. Required before public push requests. |
| pushTokenProvider | Optional | Token provider used by current-device helpers. |
| pushPermissionProvider | Optional | Custom permission resolver. Useful for iOS or app-owned permission flows. |
| deviceInfoProvider | Optional | Adds custom device metadata. |
| deviceMeta | Optional | Static metadata merged into every registration/state metadata payload. |
| fetcher | Optional | Custom fetch implementation. |
| headers | Optional | Extra headers added to SDK requests. |
| offlineSyncQueue | Optional | Enables/configures retry queue for sync operations. |
| onIdentityChanged | Optional | Callback fired when local identity changes. |
| onPushSubscriptionSynced | Optional | Callback fired after successful sync. |
| onPushSubscriptionSyncFailed | Optional | Callback fired after failed sync. |
| publicAuth | Optional | Advanced signing overrides for tests or custom signing. |
| requestIdFactory | Optional | Custom request id generator for registration/state operations. |
| runtimeConfig | Optional | Override API paths or default headers. Mostly for tests/versioning. |
| sdkVersion | Optional | Overrides default SDK version metadata. |
If the admin panel rotates the signed SDK auth token while the app is running, update the existing client:
mrd.setSdkAuthToken(nextSdkAuthToken);Step 9: Validate Admin App Metadata
Call initialize() during startup if you want to fail early when the admin app
is disabled, missing credentials, or missing platform support:
await mrd.initialize();This calls:
GET /api/public/push/apps/{appId}Step 10: Identify The Current User
When the app knows the current user, send identity information to the backend:
await mrd.login({
externalId: 'customer-42',
email: '[email protected]',
phone: '+6281234567890',
firstName: 'Jane',
lastName: 'Tester',
attributes: {
country: 'ID',
language: 'id',
},
tags: {
plan: 'pro',
lifecycle: 'trial',
},
});This calls:
PUT /api/public/push/apps/{appId}/users/identifyexternalId is strongly recommended because user readback and tag endpoints are
keyed by externalId.
Step 11: Register And Sync The Current Device
For most apps, call initializeAndSyncCurrentDevice() after login or when the
app has a stable identity:
await mrd.initializeAndSyncCurrentDevice({
externalId: 'customer-42',
email: '[email protected]',
tags: {
plan: 'pro',
locale: 'id',
},
});That one call:
- Fetches push app metadata.
- Checks app enabled, credentials, and platform support.
- Identifies or updates the user.
- Resolves the push token through
pushTokenProvider. - Registers the device subscription.
The API sequence is:
GET /api/public/push/apps/{appId}
PUT /api/public/push/apps/{appId}/users/identify
POST /api/public/push/apps/{appId}/subscriptionsUse identify: false only when the user was just identified and you explicitly
want to skip the identify call:
await mrd.initializeAndSyncCurrentDevice({
externalId: 'customer-42',
identify: false,
});Step 12: Register With An Explicit Token When Needed
If the call site already has the token and platform, use explicit-token sync:
await mrd.initializeAndSync({
token: fcmToken,
platform: 'android',
externalId: 'customer-42',
osPermission: 'granted',
tags: {
plan: 'pro',
},
});Or use the lower-level registration helper:
await mrd.registerPushSubscription({
token: fcmToken,
platform: 'android',
externalId: 'customer-42',
status: 'subscribed',
});Step 13: Handle Firebase Token Refresh
The app owns Firebase token refresh listeners. When Firebase gives you a new token, force a sync with that token:
messaging().onTokenRefresh(async (token) => {
await mrd.initializeAndSyncCurrentDevice({
token,
externalId: 'customer-42',
forceSync: true,
});
});Passing token to a current-device method overrides pushTokenProvider for
that call.
Step 14: Update Permission Or Token State Changes
When the user disables notifications, the token becomes invalid, or your app wants to send a heartbeat-style update, call a token-state method:
await mrd.updateCurrentDevicePushSubscriptionState({
status: 'unsubscribed',
osPermission: 'denied',
backendCode: 'PERMISSION_DENIED',
reason: 'User disabled notifications in settings',
meta: {
source: 'settings_screen',
},
});This calls:
PUT /api/public/push/apps/{appId}/subscriptions/by-token/{urlEncodedToken}The SDK URL-encodes the token and signs the encoded path.
Step 15: Manage Tags
Tags can be sent after login or queued before identity exists.
Queue tags before login:
await mrd.addTags({
lifecycle: 'trial',
plan: 'pro',
});
await mrd.login({
externalId: 'customer-42',
});Send tags directly for a known user:
await mrd.addTags(
{
plan: 'enterprise',
},
{
externalId: 'customer-42',
mode: 'merge',
}
);
await mrd.removeTags(['lifecycle'], {
externalId: 'customer-42',
});Tag endpoints:
PUT /api/public/push/apps/{appId}/users/{externalId}/tags
DELETE /api/public/push/apps/{appId}/users/{externalId}/tagsStep 16: Read Back User State
Read the backend user state and tags when you need to verify what is stored:
const user = await mrd.getUser({
externalId: 'customer-42',
});
const tags = await mrd.getTags({
externalId: 'customer-42',
});This calls:
GET /api/public/push/apps/{appId}/users/{externalId}Step 17: Add React Provider Only If Components Need SDK State
The SDK can be used without React context. Add MrdSdkProvider only when
components need readiness state, refresh(), or the shared client.
For the most complete provider setup, create the client yourself and pass it
through client. This keeps pushTokenProvider and other client-level
providers available to methods called from React components.
import messaging from '@react-native-firebase/messaging';
import { Button, Platform } from 'react-native';
import {
MrdSdkProvider,
MrdSdkReadyBoundary,
useMrdSdk,
createMrdSdkClient,
} from 'react-native-mrd-sdk';
const mrd = createMrdSdkClient({
apiBaseUrl: 'https://foo.localhost:5173',
appId: 'mrd_htajyzzysucj41h6omw9wvci',
sdkAuthToken: 'mrd_sdk_REPLACE_WITH_ADMIN_PANEL_TOKEN',
pushTokenProvider: async () => ({
platform: Platform.OS === 'ios' ? 'ios' : 'android',
provider: 'firebase',
token: await messaging().getToken(),
}),
});
function PushRegisterButton() {
const sdk = useMrdSdk();
async function register() {
await sdk.client.initializeAndSyncCurrentDevice({
externalId: 'customer-42',
tags: {
plan: 'pro',
},
});
}
return <Button title="Register push" onPress={register} />;
}
export function App() {
return (
<MrdSdkProvider
apiBaseUrl="https://foo.localhost:5173"
appId="mrd_htajyzzysucj41h6omw9wvci"
sdkAuthToken="mrd_sdk_REPLACE_WITH_ADMIN_PANEL_TOKEN"
client={mrd}
>
<MrdSdkReadyBoundary loadingFallback={null} errorFallback={null}>
<PushRegisterButton />
<RootApp />
</MrdSdkReadyBoundary>
</MrdSdkProvider>
);
}Provider state contains:
| Field | Description |
| --- | --- |
| client | The MrdSdkClient instance. |
| apiBaseUrl | Current base URL. |
| appId | Current app id. |
| status | idle, loading, ready, or error. |
| pushApp | Normalized app metadata when ready. |
| error | Initialization error when status is error. |
| refresh() | Re-runs provider initialization. |
MrdSdkReadyBoundary renders children only when provider status is ready.
MrdSdkBootstrapBoundary is retained as a compatibility alias.
Step 18: Verify The Integration
Use this checklist before marking the client integration done:
- Firebase is installed and configured in the app.
messaging().getToken()returns a token on the target device.apiBaseUrl,appId, andsdkAuthTokenare from the same tenant.mrd.initialize()succeeds.mrd.initializeAndSyncCurrentDevice()succeeds with a valid identity.- Firebase token refresh calls sync the refreshed token.
- Permission changes call
updateCurrentDevicePushSubscriptionState(). - Notification open handling is implemented in the app.
- Optional payload helpers are used if the app needs MRD launch URL/button parsing.
API Reference
createMrdSdkClient(options)
Creates an MrdSdkClient.
Required for network use:
apiBaseUrlappIdsdkAuthToken
Common optional:
pushTokenProviderpushPermissionProviderdeviceMetadeviceInfoProviderofflineSyncQueue- lifecycle callbacks
client.initialize(appId?)
Fetches and normalizes push app metadata.
Returns Promise<MrdSdkPushApp>.
client.getPushApp(appId?)
Same network operation as initialize. Useful when you want a method name that
describes the read operation directly.
Returns Promise<MrdSdkPushApp>.
client.login(identity)
Identifies or updates a user, stores the identity locally, notifies identity
listeners when it changes, and flushes queued tag mutations when externalId
exists.
Required:
- at least one of
externalId,email, orphone
Optional:
appIdfirstNamelastNameattributestags
Returns Promise<MrdSdkIdentifyUserResult>.
client.identifyUser(identity)
Sends the identify request and normalizes the result. It does not perform the
same local identity lifecycle work as login.
Returns Promise<MrdSdkIdentifyUserResult>.
client.logout()
Clears local SDK identity and notifies identity listeners. It does not call the backend.
client.getCurrentIdentity()
Returns the locally stored identity, or undefined.
client.onIdentityChanged(callback)
Registers an identity listener.
Returns an unsubscribe function.
const unsubscribe = mrd.onIdentityChanged((event) => {
console.log(event.previousIdentity, event.currentIdentity);
});
unsubscribe();client.getCurrentDevicePushToken(options?)
Resolves the current device token.
Optional:
appIdforceRefreshplatformtoken
If token is passed, the SDK returns it directly. Otherwise, a
pushTokenProvider must be configured.
Returns Promise<MrdSdkPushTokenProviderResult>.
client.registerCurrentDevicePushSubscription(options)
Registers the current device token resolved from pushTokenProvider, unless
token is passed directly.
Required:
- at least one of
externalId,email, orphone pushTokenProvider, unlesstokenis passed- resolvable
platform, either passed or returned bypushTokenProvider
Optional:
appIdtokenplatformforceRefreshTokenfirstNamelastNameattributestagsstatusosPermissionbackendCoderequestIdsdkVersionmeta
Returns Promise<MrdSdkCurrentDevicePushSubscriptionRegistrationResult>.
client.registerPushSubscription(options)
Registers an explicit token.
Required:
tokenplatform- at least one of
externalId,email, orphone
Optional:
appIdfirstNamelastNameattributestagsstatusosPermissionbackendCoderequestIdsdkVersionmeta
Returns Promise<MrdSdkPushSubscriptionRegistrationResult>.
client.initializeAndSyncCurrentDevice(options)
High-level current-device orchestration:
- Reads push app metadata.
- Verifies app status, credentials, and platform support.
- Identifies the user unless
identify: false. - Registers the current device token.
Required and optional registration fields are the same as
registerCurrentDevicePushSubscription.
Additional optional:
identifyforceSyncenqueueOfflineRetryonPushSubscriptionSyncedonPushSubscriptionSyncFailed
Returns Promise<MrdSdkInitializeAndSyncCurrentDeviceResult>.
client.initializeAndSync(options)
High-level explicit-token orchestration. Same as
initializeAndSyncCurrentDevice, but requires token and platform.
Returns Promise<MrdSdkInitializeAndSyncPushSubscriptionResult>.
client.initializeAndSyncPushSubscription(options)
Alias for initializeAndSync(options).
client.syncPushSubscription(options)
Runs the same sync flow as initializeAndSync, including app readiness checks,
optional identify, deduping, callbacks, and optional offline retry queueing.
Use this when the name better fits your code path. Most integrations can call
initializeAndSync or initializeAndSyncCurrentDevice directly.
Returns Promise<MrdSdkPushSubscriptionSyncResult>.
client.updateCurrentDevicePushSubscriptionState(options)
Updates status/permission metadata for the current device token.
Required:
pushTokenProvider, unlesstokenis passed- resolvable
platform, either passed or returned bypushTokenProvider
Optional:
appIdtokenplatformforceRefreshTokenstatusosPermissionbackendCodereasonrequestIdmeta
Returns Promise<MrdSdkCurrentDevicePushSubscriptionStateUpdateResult>.
client.updatePushSubscriptionState(options)
Updates status/permission metadata for an explicit token.
Required:
tokenplatform
Optional:
appIdstatusosPermissionbackendCodereasonrequestIdmeta
Returns Promise<MrdSdkPushSubscriptionStateUpdateResult>.
client.addTags(tags, options?)
Adds or replaces tags. If no externalId is known, the mutation is queued
locally.
Required:
tagsobject
Optional:
appIdexternalIdmode:mergeorreplace, defaultmerge
Returns Promise<MrdSdkUserTagsResult>.
client.removeTags(tagKeys, options?)
Deletes tags. If no externalId is known, the mutation is queued locally.
Required:
- non-empty
tagKeysarray
Optional:
appIdexternalId
Returns Promise<MrdSdkUserTagsResult>.
client.getTags(options?)
Returns backend tags for an externalId, or local queued tags when no
externalId is known.
Optional:
appIdexternalId
Returns Promise<Record<string, unknown>>.
client.getUser(options?)
Reads backend user state by externalId.
Required:
externalId, either passed directly or available from local identity
Optional:
appId
Returns Promise<MrdSdkPublicUserState>.
client.requestPublicPushJson(options)
Low-level signed request helper for public push endpoints that do not yet have a first-class method.
Required:
methodpathsdkAuthTokenconfigured on the client
Optional:
appIdbodyheaders
The helper signs the request and returns parsed JSON. It validates the HTTP method but does not normalize the response to a high-level SDK type.
client.setSdkAuthToken(newToken)
Rotates the local SDK signing token.
Returns the normalized token string.
client.getSdkAuthToken()
Returns the current SDK auth token, or undefined.
client.getApiBaseUrl()
Returns the resolved API base URL.
client.getAppId()
Returns the default app id configured on the client, or undefined.
client.flushOfflineSyncQueue()
Attempts to flush queued sync retries immediately.
client.getOfflineSyncQueueEntries()
Returns queue entry summaries containing:
idappIdattemptsnextAttemptAt
client.getOfflineSyncQueueSize()
Returns the number of queued sync retry entries.
initializeMrdSdk(options)
Creates or uses a client, calls initialize, and returns a ready React-style
state object.
Required:
apiBaseUrlappIdsdkAuthToken
Optional:
client- most client setup fields used for initialization
autoInitializewhen used through provider/hook setup
useMrdSdk()
Reads the nearest MrdSdkProvider context.
Throws MrdSdkError with code MRD_SDK_CONTEXT_MISSING when used outside the
provider.
useMrdSdkReadyState()
Returns provider state only when status is ready; otherwise returns null.
useMrdSdkInitialization(options)
React hook used by MrdSdkProvider. Most apps should use MrdSdkProvider
instead of calling this hook directly.
MrdSdkProvider
Creates SDK context and initializes app metadata by default.
Required:
apiBaseUrlappIdsdkAuthTokenchildren
Optional:
clientautoInitialize- initialization callbacks and request options
When you need current-device helpers from React components, prefer passing a
prebuilt client with pushTokenProvider.
MrdSdkReadyBoundary
Renders:
idleFallbackwhen state isidleloadingFallbackwhen state isloadingerrorFallbackwhen state iserrorchildrenwhen state isready
MrdSdkBootstrapBoundary
Compatibility alias for MrdSdkReadyBoundary.
Types And Accepted Values
Platforms
type MrdSdkPushPlatform = 'ios' | 'android' | 'web';For normal React Native push registration, use ios or android.
Subscription Status
type MrdSdkPushSubscriptionStatus =
| 'subscribed'
| 'unsubscribed'
| 'pending'
| 'invalid';Default registration status is:
unsubscribedwhenosPermissionisdeniedsubscribedotherwise
OS Permission
type MrdSdkPushOsPermission =
| 'granted'
| 'denied'
| 'unknown'
| 'provisional';Identity
At least one identity field is required for identity, registration, and sync:
{
externalId?: string;
email?: string;
phone?: string;
}Prefer externalId because tag and user readback endpoints are keyed by
externalId.
Tags
Most SDK tag helpers expect an object:
{
plan: 'pro',
lifecycle: 'trial',
beta: true,
}Registration also accepts a tag array for backend-compatible payloads, but SDK local tag snapshots are object-based.
Device Metadata
The SDK sends best-effort metadata under meta, including values such as:
runtime_platformapp_nameapp_versionbuild_numberbundle_iddevice_branddevice_modeldevice_typesystem_namesystem_versionos_versiontime_zonereact_native_versionapi_levelis_tabletis_emulator
Unavailable fields are omitted. If metadata collection fails, the SDK falls back to minimal metadata and continues.
Signed Request Contract
All public admin-panel calls are signed by the SDK. Client apps never need to build signing headers manually.
The SDK sends:
X-MRD-App-IDX-MRD-TimestampX-MRD-NonceX-MRD-SignatureX-MRD-Signature-Version
Canonical string:
METHOD
/api/public/push/apps/{appId}/...
appId
unixTimestampSeconds
nonce
sha256(rawRequestBody)Important signing details:
- The signed path always starts with
/api/public/.... - If
apiBaseUrlalready includes/api, the request URL avoids duplicating/api. - Token-state updates URL-encode the token in the path and sign the encoded path.
- The SDK retries once for bounded auth-freshness errors such as expired timestamp or replay nonce.
Default public paths:
| Operation | Path |
| --- | --- |
| Push app lookup | /api/public/push/apps/:appId |
| Identify user | /api/public/push/apps/:appId/users/identify |
| Register subscription | /api/public/push/apps/:appId/subscriptions |
| Update token state | /api/public/push/apps/:appId/subscriptions/by-token/:token |
| Read user | /api/public/push/apps/:appId/users/:externalId |
| Mutate tags | /api/public/push/apps/:appId/users/:externalId/tags |
Error Handling
SDK-originated errors use MrdSdkError:
import { MrdSdkError } from 'react-native-mrd-sdk';
try {
await mrd.initializeAndSyncCurrentDevice({
externalId: 'customer-42',
});
} catch (error) {
if (error instanceof MrdSdkError) {
console.log(error.code, error.message, error.cause);
}
}Common error codes:
| Code | Meaning |
| --- | --- |
| MRD_SDK_PUBLIC_AUTH_MISSING | sdkAuthToken was not configured before a signed request. |
| MRD_SDK_FETCH_UNAVAILABLE | No global fetch exists and no custom fetcher was supplied. |
| MRD_SDK_INVALID_HTTP_METHOD | Request method is not one of GET, POST, PUT, PATCH, or DELETE. |
| MRD_SDK_INVALID_REQUEST_BODY | Request body could not be serialized to JSON. |
| MRD_SDK_INVALID_RESPONSE_JSON | Response JSON could not be read. |
| MRD_SDK_INVALID_PAYLOAD | Backend payload shape did not match the SDK contract. |
| MRD_SDK_INVALID_PUSH_PLATFORM | Platform is not ios, android, or web. |
| MRD_SDK_INVALID_PUSH_SUBSCRIPTION_STATUS | Status is not one of the supported subscription statuses. |
| MRD_SDK_INVALID_PUSH_OS_PERMISSION | Permission is not one of the supported permission values. |
| MRD_SDK_INVALID_PUSH_TAGS | Tags or tag keys are malformed. |
| MRD_SDK_PUSH_IDENTITY_REQUIRED | No externalId, email, or phone was provided. |
| MRD_SDK_PUSH_TOKEN_PROVIDER_MISSING | Current-device method needs pushTokenProvider or a direct token. |
| MRD_SDK_PUSH_PLATFORM_REQUIRED | Current-device method could not infer platform. |
| MRD_SDK_PUSH_APP_DISABLED | Admin-panel push app is disabled. |
| MRD_SDK_PUSH_APP_CREDENTIALS_MISSING | Admin-panel push app has no Firebase credentials. |
| MRD_SDK_PUSH_PLATFORM_DISABLED | Target platform is disabled for the push app. |
| MRD_SDK_PUBLIC_PUSH_REQUEST_FAILED | Backend request failed. Status/body/backend code are preserved in cause when available. |
| MRD_SDK_CONTEXT_MISSING | useMrdSdk was used outside MrdSdkProvider. |
Offline Retry Queue
The offline retry queue is optional. It applies to sync operations and is meant for transient network/server failures.
const mrd = createMrdSdkClient({
apiBaseUrl,
appId,
sdkAuthToken,
pushTokenProvider,
offlineSyncQueue: {
enabled: true,
storage: AsyncStorage,
baseDelayMs: 2000,
maxDelayMs: 120000,
maxAttempts: 6,
},
});Storage adapter shape:
type MrdSdkKeyValueStorageAdapter = {
getItem: (key: string) => Promise<string | null> | string | null;
setItem: (key: string, value: string) => Promise<void> | void;
removeItem?: (key: string) => Promise<void> | void;
};Useful queue methods:
const size = mrd.getOfflineSyncQueueSize();
const entries = mrd.getOfflineSyncQueueEntries();
await mrd.flushOfflineSyncQueue();Disable queueing for one sync call:
await mrd.initializeAndSyncCurrentDevice({
externalId: 'customer-42',
enqueueOfflineRetry: false,
});Push Payload Helpers
The SDK does not install notification listeners, but it provides helpers for normalizing MRD push payload data.
import {
normalizeMrdPushPayloadData,
resolveMrdPushOpenAction,
} from 'react-native-mrd-sdk';
const data = normalizeMrdPushPayloadData(remoteMessage);
const action = resolveMrdPushOpenAction(remoteMessage, buttonActionId);normalizeMrdPushPayloadData returns:
campaignIdlaunchUrlbuttonsrawData
resolveMrdPushOpenAction returns:
campaignIdactionIdlaunchUrl
Troubleshooting
Current-device registration says token provider is missing
Create the client with pushTokenProvider, or pass token directly to the
current-device call.
await mrd.registerCurrentDevicePushSubscription({
token: fcmToken,
platform: 'android',
externalId: 'customer-42',
});Current-device registration cannot infer platform
Pass platform, or return it from pushTokenProvider.
pushTokenProvider: async () => ({
token: await messaging().getToken(),
platform: Platform.OS === 'ios' ? 'ios' : 'android',
});Push app is disabled or platform is disabled
Check the admin-panel push app:
- app is enabled
- credentials exist
- Android/iOS platform is enabled
- the
appIdmatches the app you are configuring
Requests fail after changing apiBaseUrl
apiBaseUrl can include /api or omit it. Do not include public endpoint paths
in apiBaseUrl; the SDK appends the configured public paths.
Good:
apiBaseUrl: 'https://tenant.example.com'
apiBaseUrl: 'https://tenant.example.com/api'Avoid:
apiBaseUrl: 'https://tenant.example.com/api/public/push/apps/...'iOS permission is unknown
The SDK does not own iOS notification permission prompts. Pass osPermission
from your app, or provide pushPermissionProvider.
const mrd = createMrdSdkClient({
apiBaseUrl,
appId,
sdkAuthToken,
pushTokenProvider,
pushPermissionProvider: async () => readIosPushPermissionFromYourApp(),
});Tags are queued instead of sent
addTags and removeTags queue locally until an externalId is available.
Pass externalId to the tag call or call login({ externalId }).
React hook throws context missing
useMrdSdk must be called under MrdSdkProvider.
Verification
For SDK development, run:
yarn typecheck
yarn lint
yarn test --runInBand --watchman=falseFor app integration, verify:
- Firebase is installed and configured in the app.
messaging().getToken()returns a token on a real device or valid simulator setup.apiBaseUrl,appId, andsdkAuthTokencome from the correct tenant.initialize()succeeds.initializeAndSyncCurrentDevice()succeeds after login or with an identity passed.- Token refresh calls sync the new token.
- Permission changes call token-state update.
- Notification open handling uses the app's listener and optional SDK payload helpers.
