Handling results
How to read the result of a call — immediately, from an event, or as a returned value.
The three ways a call returns
How you get a result depends on the call. There are three patterns.
- Fire-and-forget. UI and navigation calls (
bdk.navigation.navigate,bdk.ui.showBanner) just do something.awaitconfirms the call went out; there's nothing else to read. - Result on an event. Interactive calls (media, location, pickers, biometrics, purchases) finish later. Subscribe with
bdk.on(...)to get the data. - Returned value. Server helpers (
@bdk/native/server/*) run on your backend andawaitreturns the real value.
Models 1 and 2 run in the browser and resolve to a NativeCommandResult. Model 3 returns a typed value.
Browser command options are passed straight to native — match the key names exactly (e.g. { id, type } for purchases).
Fire-and-forget calls
Use these when the app just does something visible — navigate, show a toast, vibrate. await resolves to a NativeCommandResult confirming the call went out. There's no event to listen for.
Outside the app these calls resolve with triggered: false instead of throwing — pending in a browser tab, skipped with no window — so your web build keeps working.
Get a result from an event
Use this when the user has to act — take a photo, pick a date, approve Face ID, confirm a purchase. Subscribe to the event before you make the call; the data arrives on the listener, not on the await.
Event names don't always match the method:
bdk.media.capturePhoto()emitsphotoCaptured.bdk.media.pickPhoto()emitsphotoSelected.bdk.location.getCurrentPosition()emitslocation.bdk.ui.pickDateTime()emitsdatePicked.bdk.auth.authenticateBiometrics()emitsbiometricResult.bdk.iap.purchaseIos()/purchaseAndroid()emitpurchaseSuccessorpurchaseFailed.
capturePhoto emits photoCaptured, not photoSelected (which belongs to pickPhoto). Listening on the wrong one is the top reason a result "never arrives".
bdk.on(event, listener) returns an unsubscribe function — call it to stop listening.
Server calls that return a value
Use these on your backend for receipt verification, link creation, and push notifications. They're fully typed and await returns the real result — no event needed.
For example, verifyIosReceipt returns an IosReceiptValidationResult with isValid, resultData, errorData, and raw.
Never import @bdk/native/server/* into a browser bundle — those modules use credentials and Node APIs. The browser-safe entry points are the package root and @bdk/native/browser.
The NativeCommandResult shape
Every browser call (Models 1 and 2) resolves to a NativeCommandResult. Use it to confirm the call went out, or to detect that you're running on the web.
interface NativeCommandResult {
command: string; // the native command name that was dispatched
queued: boolean; // was it added to the queue?
triggered: boolean; // was it actually handed to the app?
skipped: boolean; // was it dropped without triggering?
pending?: boolean; // queued, waiting for the agent to become available
reason?: string; // why it was skipped / pending, when applicable
}
The common case is { triggered: true }. Outside the app triggered is false — pending in a browser tab, skipped with no window — so branch on !triggered to degrade gracefully.
A few commands are platform-specific (e.g. bdk.iap.purchaseIos is iOS only). On a device that can't run one, the call rejects with a BdkError — wrap it in try/catch, or check bdk.isNative() first.
Why await isn't the result
Coming from fetch(), unlearn one habit: for browser calls, await does not give you the data — it confirms the call was dispatched.
- Model 1: nothing more to wait for — the
NativeCommandResultis the whole result. - Model 2: the real outcome arrives later through
bdk.on(...). Readingresult.fileUrloff theawaitnever works. - Model 3: the only one where
awaitreturns the value.
The rule: await to confirm dispatch, subscribe to receive data.