Mobile handoff examples
Mobile consumes only the integrating partner's backend projection. It never imports @chiahealth/partner and never receives a Chia API key, webhook secret, authenticated asset URL, raw catalog object, or patient/order status.
Platform snippets#
| Platform | System-browser implementation |
|---|---|
| iOS / UIKit | ios/Handoff.swift |
| Android | android/Handoff.kt |
| React Native | react-native/Handoff.tsx |
| Flutter | flutter/handoff.dart |
Each snippet opens HTTPS with the OS external-browser API and includes an optional generic return handler that ignores query and fragment data.
The executable web reference and closed backend projection live in examples/partner-reference-backend/src/renderer-assets.ts and renderer-contract.ts. Every supported shell uses the same reviewed renderer-conformance.v1.json decision, interaction, accessibility, and forbidden-behavior cases. Run them from the bundle root with:
python3 scripts/partner_contracts/check_renderer_conformance.py
Input contract from the partner's backend#
The backend must already have:
- fetched/cached the catalog on a schedule;
- verified and mirrored the image at a neutral partner-owned URL;
- derived fresh authenticated partner account service-state evidence;
- called
resolveCatalogHandoff; - removed hidden items and suppressed the session action for
neutral_only.
Mobile receives presentation fields plus one closed handoff decision:
type MobileHandoff =
| {
mode: "neutral_only";
heading: string;
body: string;
actionLabel: "Continue to Chia";
url: string;
}
| {
mode: "share_or_neutral";
heading: string;
body: string;
recipient: { legal_name: string; privacy_contact: string };
dataShared: readonly string[];
purpose: string;
compensationDisclosure: string;
policyUrls: Record<string, string>;
primary: {
actionLabel: "Share selection and continue";
sessionCreationPath: "/v2/handoff-sessions";
disclosureVersion: string;
};
secondary: {
actionLabel: "Continue without sharing";
url: string;
};
};
Do not generalize the action-label literals or add an arbitrary URL field. Refresh the backend projection at tap time if local service-state evidence or the publication may have expired.
Tap sequence#
- Render every required catalog field before the CTA in
presentation_contract.block_order. - On CTA tap, fetch/resolve fresh partner-local eligibility without calling or preloading Chia.
- Render the API-supplied JIT heading, body, recipient/data/purpose/compensation/policy links, and exact actions.
- For no-share, pass the neutral URL directly to the platform snippet. For share, send the catalog ID, disclosure version,
affirmed:true, and affirmative timestamp to the partner's authenticated backend/handoff-sessionendpoint with a persisted random UUIDv4 idempotency key. - Open only the short-lived URL returned to that initiating response; never store it.
- Let the OS browser own the Chia experience. Do not observe it.
Disable both actions synchronously before starting the share request. Duplicate taps join that one in-flight operation. Keep the exact body and UUIDv4 after a network error, 429, or server error; discard them after a definitive client rejection and fetch a new projection before another attempt. On app foreground or restored navigation, fetch a current projection and render it again without using focus timing as an outcome signal. Never persist the returned URL.
For share_or_neutral, Share selection and continue and Continue without sharing must both remain usable. For neutral_only, no session-creation action may exist. The generated handoff URL exists only after affirmative share consent, expires in 15 minutes, and must never enter durable mobile state or telemetry.
Forbidden mobile behavior#
- WebView, custom-tab JavaScript bridge, or embedded browser;
fetch,HEAD, preview, prefetch, preconnect, link expansion, or availability probing;- redirector, link shortener, URL rewrite, custom header, query parameter, or added fragment;
- persistence, clipboard capture, analytics, attribution, crash breadcrumb, screenshot tooling, or logs containing a selection/handoff URL;
- inference from app focus, browser return timing, cookies, or a generic return;
- any Chia SDK, API key, webhook secret, finance data, authenticated asset URL, or lifecycle/status call.
Treat the opaque phs_… URL as sensitive short-lived routing context, even though it is not an authentication credential. The OS browser receives it only after an explicit user action; no status lookup or patient/order callback exists.
Generic return#
If separately approved and registered, a single generic partner Universal Link/App Link may return the user to marketplace home. The handler validates only the registered return host, ignores the entire query and fragment, and never displays or records a completion status. The return action must be identical regardless of abandonment, eligibility, payment, prescription, or fulfillment.
There is no automatic callback. A production share URL enters Chia's live co-branded journey; a generic return, when separately approved, always returns to marketplace home without communicating whether the user completed identity, intake, payment, care, or fulfillment.
Mobile test matrix#
- all required card fields fit supported accessibility text sizes without truncation;
hiddennever renders,neutral_onlyhas one action,share_or_neutralhas two equal-access actions;- stale/missing/unsupported service-state evidence fails safely;
- each supported platform opens an external OS browser, never an embedded view;
- instrumentation/traffic capture shows no request before tap and no URL in telemetry;
- invalid/expired context remains generic Chia and produces no partner callback;
- generic return ignores crafted query/fragment values and routes only to marketplace home.
- background and restored views refresh the projection without inferring an outcome;
- double taps produce one session request and one external navigation, and an
uncertain retry reuses the exact body and idempotency key;
- 200% text size, screen-reader order, keyboard/switch access, reduced motion,
and a 320 CSS-pixel viewport retain full untruncated copy and every action.
See Catalog, assets, and handoff for the full publication and JIT contract.