◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/App Dev
◇ App Dev/2026-09-07Intermediate

Sending user text to an external AI: what actually goes in Play's Data safety form

Classifying user input sent to an external AI as collected, shared, or ephemeral in Play's Data safety form, plus a redaction ordering bug found by running the code.

Google Play40Data safetyPrivacy10AI31Indie development3Android59

✦ Premium Article

I added a small text box to an app: write down what is bothering you, and an AI tidies it up and hands it back. The implementation took half a day. What stopped me was the step after that. I opened the Data safety section in Play Console and sat there, unsure which row this text box belonged to.

Nothing gets stored on my own server. The text goes to an external model, the reply is rendered, and that is the end of it. So "not collected" should be fine — that was my first instinct.

It was not enough. The classification does not hinge on whether I keep the data. It hinges on what the recipient does with it. I ship a handful of apps as an indie developer, and before I understood that, I pushed several updates where the form and the actual traffic had quietly drifted apart. Here is the order I now work through.

Whether it counts as "shared" depends on the recipient, not the sender

In Play's vocabulary, data leaving the device is collected, and data reaching a third party is shared. The part that trips people up is the second one.

Transmission alone does not settle it. What matters is whether the recipient uses the data for their own purposes, and how long they hold it. Ad targeting, cross-app profiling, benchmarking — if any of those apply on their side, it is sharing.

External AI providers sit right on that line. Most state that API traffic is not used for training, yet retaining logs for a fixed window to monitor abuse is common practice. "Not used for training" and "not retained" are two different claims.

My code decides what gets sent. The recipient's retention window decides how it gets declared. I reread that line every time I add a destination.

There is a harder part, too: your own fetch calls are not the only way out. Ad SDKs and crash reporters each make their own decisions. If an SDK in your app collects or shares data, that belongs in the form as well. I walked through auditing the SDK chain in auditing the Privacy Manifest of a Rork-generated Expo app all the way down the SDK chain.

Ephemeral does not mean you skip the question

Here is the second thing I had backwards.

Play recognises ephemeral processing: data sent off device, held only in memory, and kept no longer than needed to serve that request in real time. The canonical example is a weather app passing location to fetch a local forecast.

The crucial detail is that ephemerally processed data still gets answered in the form. What meeting the standard buys you is that the item is not surfaced on your store listing. It does not buy you a blank field.

Read it as "ephemeral, so I don't have to write it" and you ship a form that disagrees with your app's behaviour — a disagreement you will not notice until review points at it. For a while I skipped that same field on every update.

Ephemeral status is also fragile in a way that has nothing to do with policy. The moment you log a request and response to check an answer, the "not retained" premise is gone. A debugging line can change the classification.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦You will be able to inventory everything your app sends off device, including the traffic your own code never wrote
✦You will avoid the rejection loop that comes from assuming ephemeral processing means you can leave the answer blank
✦You will be able to decide whether user input sent to an external model counts as collected, shared, or ephemeral, based on the recipient's retention window
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◇ App Dev2026-08-18
The three places I had to fix before a Rork project actually targeted API level 36
My app.json said targetSdkVersion 36. The value my build actually read was 35. Here is the script that reports the effective value, and how I split my apps between raising, leaving alone, and requesting an extension.
◇ App Dev2026-10-21
expo-widgets Works on iOS but Fails Only in the Android Build — Pin the Version or Exclude It?
Reports say expo-widgets 58.0.17 and later stops at Kotlin compilation on Android only. How to recognize it, when to pin versus exclude from autolinking, and a script that checks your project.
◇ App Dev2026-10-18
It Ran Fine in Debug, but the Release Build Died on Launch with a Resource Not Found. A File I Looked Up by Name Had Been Shrunk Away
A release-only crash at launch, caused by images and sounds looked up by a name string being removed by resource shrinking. A keep.xml written at prebuild, plus a post-build check that confirms it worked.