1. The premise
Gift-card data is unusual: it's mundane enough that people assume it's harmless, and sensitive enough that a leaked card number and PIN gets drained in minutes on a forum. Most wallet apps split the difference by copying it to the cloud "for sync" and calling it a feature.
Cue's architecture starts from the opposite assumption: this data doesn't need to leave your phone for the app to work.
There are now four ways to get a card into the wallet: the camera, a photo-library import, voice entry, and the iOS Share Sheet from Mail. The Share Sheet path stays fully on-device. Camera and photo-library scans are read on-device first, then by default sent securely to our AI for richer extraction, which you can turn off with "Scan on-device only".
2. Where your data lives
What is not in the cloud by default
- Your wallet
- Your card balances
- Your card numbers or PINs
- Your scan history
- Your location (Smart Profiles sends only a 1km-rounded grid cell for its density lookup, never a precise coordinate)
- Your notification decisions
- Your dismiss history
- Your quiet zones
3. Authentication is optional
Cue's Supabase-backed account layer exists for users who want backup and cross-device sync. It is opt-in. The entire core app, scanner, wallet, geofence notifications, widgets, watch app, and live activity, works without ever creating an account. (CarPlay is on the roadmap and will ship under the same no-account default.)
When you do sign up, what gets synced is an encrypted blob of card data and the metadata the server needs to route sync messages (your user ID, a last-updated timestamp). The server cannot read your card numbers; they are encrypted client-side before upload.
When you delete your account, the server-side erase is initiated and the local SwiftData store is emptied. Account deletion does not require emailing support, it's a button in Settings.
Family Sharing follows the same posture. One Pro subscription covers your Apple Family, and each member keeps their own private wallet: family members never see each other's cards, balances, or locations.
4. Secrets are biometric-gated
A GiftCard has a hasSecrets: Bool flag set whenever a card number or PIN is present. Every view that renders those fields wraps the read in LAContext:
private func revealCardNumber() {
let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Show your card number") { success, _ in
if success {
DispatchQueue.main.async { self.numberIsVisible = true }
}
}
}
The SwiftData store itself is not encrypted at a per-field level, we rely on iOS's standard file-level encryption (Data Protection: Complete Until First User Authentication). The biometric prompt is a presentation gate, not a storage gate. What this buys us: a shoulder-surfer who picks up your unlocked phone can browse your wallet list, but cannot reveal a card number without your face or fingerprint.
5. The App Group is read-only for extensions
Widgets, the Watch complication, and the background scan scheduler all read from App Group: group.com.cardcue.app. They never write to SwiftData. They publish timestamps (throttle state) and snapshots (CardCueWidgetData) through the App Group as a one-way channel from the main app outward.
This is important because widget extensions on iOS run in a restricted sandbox where a SwiftData container write can corrupt the main app's store. CardCue Pro avoids the risk entirely, the extension's only job is to read the JSON snapshot the main app wrote on its last refresh.
Card numbers and PINs are not in the widget snapshot. The widget can display balance, name, color, expiry, nothing that would be sensitive on a lock screen.
The same one-way App Group pattern also carries the iOS Share-from-Mail extension (built and shipping today). The extension writes a pending intake payload into group.com.cardcue.app.shared and the main app drains it on the next foreground. The payload is local, scoped, and ephemeral. If the app is killed mid-intake, the payload waits in the App Group until the next drain; it never leaves the device, and it never reaches any process besides the main app binary. (The user-facing Share flow itself is documented in the Scanner kit.)
The Share Extension target carries no Claude API key. The key lives in the main app binary only, because only the main app ever has a reason to call out. The extension's Info.plist has no key, the extension binary has no key, and there is no network code path inside the extension to use one. Parsing is done with regex over the email text, text that is already on the user's device because Mail has already rendered it. The extension is, by construction, incapable of contacting Anthropic. It writes a local payload, and that is the extent of its reach.
The Share Extension's physical limits
- No API key in the extension's
Info.plistor binary. - No network entitlement used by the extension.
- Parsing is regex over text Mail already rendered locally.
- The App Group is the only surface the extension writes to.
- The main app is the sole reader and drainer of that payload.
6. What Anthropic (Claude) sees
When you use the AI scanner, Cue sends:
- The single card photo you just captured.
- A prompt asking for structured JSON extraction.
Cue does not send:
- Your name, email, or user ID.
- Your device identifier.
- Your location.
- Any other card in your wallet.
- Any usage history.
The request goes to Anthropic's Claude API to extract the card details. A zero-retention agreement with Anthropic is not currently in place: under Anthropic's default commercial terms, the image may be retained for up to 30 days for trust and safety, then deleted. Anthropic does not use API inputs to train its models. See our Privacy Policy for the current status.
If you don't trust the cloud path, there is an app-wide setting "Scan on-device only" that disables the Claude engine entirely. With that enabled, every scan goes through VisionOCRScanner and never leaves the phone, at the cost of slightly less rich extraction for unusual cards. On our roadmap, targeted mid-2027: card reading that runs entirely on your device, so the default path and the private path become the same path.
7. No third-party tracking SDKs
CardCue Pro ships with:
- No Google Analytics.
- No Facebook SDK.
- No Adjust, AppsFlyer, Braze, Mixpanel, Amplitude.
- No crash reporter that phones home (we use Apple's built-in
MetricKitfor crash + hang reports, which stays in the Apple analytics pipeline the user controls via Settings → Privacy → Analytics). - No advertising identifier (IDFA) access.
- No ATT prompt, because there's nothing to ask about.
Our Info.plist purpose strings are honest and specific, NSLocationAlwaysAndWhenInUseUsageDescription describes precisely what background location powers (geofence nudges for nearby gift cards), not a vague "improve your experience."
8. App Store Privacy Nutrition Label
Cue's Privacy Label reflects the above:
| Category | Status |
|---|---|
| Data Not Collected | Contact Info, Health & Fitness, Financial Info, Sensitive Info, Browsing History, Search History, Diagnostics, Purchases, Usage Data (when not signed in) |
| Data Linked to You (opt-in, account-holders only) | Email for account recovery; User ID for sync |
| Data Used to Track You | None |
The corresponding metadata lives in docs/AppPrivacyLabels.md and is kept in sync with every App Store submission.
9. CCPA / LGPD / PIPEDA / APPI / POPIA / DPDP readiness
| Requirement | Cue's implementation |
|---|---|
| Right to access / right to export | Settings → Export data produces a JSON of the user's entire wallet, settings, and reason log |
| Right to deletion | Settings → Delete Account triggers both local wipe and server-side erase |
| Lawful basis | Performance of the contract (account-holders) and Legitimate Interest (location-based nudges, with clear opt-out) |
| Portuguese translation (LGPD) | In-flight for Brazil launch |
| App Store availability at 1.0 | U.S., Canada, Australia, New Zealand, Japan, South Korea, Singapore, South Africa, India, Mexico, Brazil, and select Latin-American markets. Not available in the EEA, the UK, or Switzerland at 1.0 pending the compliance work those jurisdictions require; re-evaluated post-launch. |
| Data Processors | Anthropic (Claude API) for card reading and the Smart Profiles density classifier (1km-rounded grid cells only); Supabase for the opt-in sync/account layer; PostHog for opt-in product analytics. No ad networks, no data brokers. |
10. Files of record
| File | Role |
|---|---|
Services/Services.swift | Scanner pipeline, Claude call-site, on-device-only toggle |
Services/AuthService.swift | Supabase auth, account deletion |
Models/Models.swift | GiftCard.hasSecrets, SwiftData schema |
Views/Cards/CardDetailView.swift | Biometric gate on reveal |
Models/Constants.swift | CardCueWidgetData shared-snapshot shape |
docs/AppPrivacyLabels.md | App Store Privacy Label source of truth |
Info.plist | Purpose strings, entitlements |
11. The stance
Privacy is a product feature, not a policy page. An app that asks for your camera, your location, your gift-card numbers, and your PINs has to earn that permission by proving, architecturally, that the data doesn't go anywhere it doesn't have to. Cue's architecture defaults to the shortest possible data path: from the card in your hand, to the phone in your pocket, and no further. That is the product's first promise, and the one the code is organized around keeping.
CardCue Pro, by Pika Product Lab LLC. Built on SwiftData, Apple's platform encryption, LocalAuthentication, and a policy of not asking for data we don't need to deliver the feature.