Cross-app handoffs
Stamp a hop from one app to another so Sessions can show where the visit continued.
Mobile and web do not share storage. Opening your website from the iOS app starts a new session, by design. A handoff does not merge those visits. It stamps the same handoff_id on both so the dashboard can link them.
Use this when someone taps Upgrade in the app and lands on billing in the browser, or when an email or SMS opens a page in your product. Call identify with the same user id on both sides so Visitors still groups the person.
bk_h is only attribution. Auth stays your login flow.How it works
- The source app calls
Brekky.handoffUrl(url). That mints a UUID, appends?bk_h=, and sends a reservedhandoff_startedevent. - Open that URL. The web snippet reads
bk_hon load, strips it withreplaceState, and sendshandoff_receivedonce. Native destinations callBrekky.consumeHandoff(url). - Later events on the destination keep
handoff_idin context untilreset()or a new hop.
In Sessions you will see a banner such as "Continued on Web App" or "Opened from iOS", with a link to the other visit when both sides have landed.
React Native (source)
BetterKnocks-style upgrade tap: build the URL, then open it. Optional properties (reason, plan) land on handoff_started.
import { Linking } from 'react-native';
const url = await Brekky.handoffUrl(
'https://app.example.com/account/billing',
{ reason: 'upgrade', plan: 'pro' },
);
await Linking.openURL(url);Web (destination)
The snippet consumes ?bk_h= automatically. You only call handoffUrl if the website is the source (for example a "open in the app" link).
const url = await Brekky.handoffUrl(
'https://app.example.com/account/billing',
{ reason: 'upgrade', plan: 'pro' },
);
window.location.assign(url);
// Destination: the snippet reads ?bk_h= on load.
// Native destinations: Brekky.consumeHandoff(url);iOS, Android, Flutter, Node
let url = Brekky.shared.handoffURL(
"https://app.example.com/account/billing",
properties: ["reason": "upgrade", "plan": "pro"]
)
UIApplication.shared.open(URL(string: url)!)
// Destination:
Brekky.shared.consumeHandoff(incomingURL.absoluteString)val url = Brekky.handoffUrl(
"https://app.example.com/account/billing",
mapOf("reason" to "upgrade", "plan" to "pro"),
)
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
// Destination:
Brekky.consumeHandoff(intent.data?.toString())final url = await Brekky.handoffUrl(
'https://app.example.com/account/billing',
{'reason': 'upgrade', 'plan': 'pro'},
);
// Open url with url_launcher (or your host).
// If Flutter is the destination:
await Brekky.consumeHandoff(incomingUrl);const url = await Brekky.handoffUrl(
'https://app.example.com/account/billing',
{ reason: 'upgrade', plan: 'pro' },
);
// Put url in an email or SMS.
// If Node is the destination (rare):
await Brekky.consumeHandoff(incomingUrl);Rules
handoff_startedandhandoff_receivedare reserved. Do not send them withtrack().- The query param is
bk_h. The value is a lowercase UUID. First writer wins per project, id, and direction. - In-app browsers often send a null referrer. The stamp is the link, not the referrer.