Trust Insights, or how your app can tell when someone is being talked through a scam
The scam that gets past every check a bank app has is the one where the victim does the work. The right person, on their own phone, signed in with Face ID, sends the money themselves, because someone on the phone has told them it is the only way to keep it safe. From the app's side, nothing is wrong. The password is right, the device is known, the person is who they say they are.
These scams are not getting rarer. They are aimed at older and less technical people, and AI has made them cheaper to run: a scammer can now work in a language they do not speak, sound fluent in it, and copy the voice of the victim's grandchild.
iOS 27 gives apps a way to ask about this. With Trust Insights, your app asks the system, before the money moves, whether it looks like someone is coaching the person through what they are doing. It gets back a risk level, and decides what to do with it.
It only helps if the apps use it. A scammer who meets a warning in one bank's app will move the victim to the bank whose app does not ask. As I write this, not one of the bank or brokerage apps on my phone uses it. So here is how, with a sample project that does all of it in one file.
What it tells you
You ask for an insight, and the one there is today is IsLikelyBeingCoachedInsight. Its value is unknown, medium or high.
unknown means the system found no sign of coaching. That is not the same as finding the transfer safe; a scam the model did not catch also comes back unknown. medium and high are reasons to slow down, and Apple's documentation adds that coaching is not always malicious. A daughter walking her father through his first transfer is coaching too.
Apple's WWDC26 session, Meet Trust Insights, says what goes into it. The signals taken from the device are not shared with Apple or with third parties, and the content of Photos, Messages and Mail is never used. The result can also take in Apple Account signals and velocity checks. What your app receives is the risk level, not the reasons for it.
The person stays in charge. They give your app permission, and they can take it away in Settings. The session says a cooldown may apply after they turn it off, to protect people who were coached into turning it off.
Not only banks
The session names the scams Apple had in mind. Tech support scams, where a fake alert leads to remote access and the victim hands over control. Authority impersonation: callers posing as the bank, a government agency or the police. Family emergencies: an urgent plea for money from a relative, more and more often in a voice made with AI. In all three, someone talks the victim through the steps by phone, chat or messages.
None of that is specific to banks. The operation categories cover any exchange of money, assets or content, in-game purchases included; changes to account and security details; costly services such as AI inference; and sending messages, submitting forms or signing documents. The session lists the moments worth asking about: large payments such as peer-to-peer transfers, actions that cannot be undone such as deleting an account or exporting personal data, permissions such as remote access or a new device, and sharing credentials or personal documents. The app that sells gift cards can ask before the purchase, and the remote support app can ask before it hands over the screen.
It only helps where an app asks. A scammer who edits the bank's web page on the victim's computer to show a refund that never happened is out of its reach, and so is a victim who drives to an ATM. The moment the victim sends the "overpaid" money back from a phone app, or buys gift cards in one, is a moment that app can ask about. What the system looks at, Apple describes only as interaction patterns, timing, context and basic sensor data.
It all happens in the app
Trust Insights is an iOS framework, and that puts some developers off: the same bank has an Android app, and a fraud team that wants one pipeline. None of that has to change. The session describes the integration as entirely client-side. The iOS app asks before the transfer and decides what to do with the answer. Your server, your Android app and your fraud rules carry on as they are, and if the fraud team wants the result, the app records it with the transfer like any other check it runs.
The app, step by step
The sample is a one-screen UIKit app for a fictional bank. Everything that touches the API is in TrustInsightsCheck.swift, in five steps, in the order you meet them. The code below is taken from it.
1. Describe the operation
An evaluation request is a context: what kind of operation this is, and which insights you want. A transfer is .payment; the other categories are .account, .resourceUse, .communication and .other.
static func context() -> CoachingContext {
InsightEvaluator.InsightContext(
operationCategory: .payment,
requestedEvaluations: IsLikelyBeingCoachedInsight.request(schema: .version1),
)
}
InsightContext takes a parameter pack, which makes its type long. The sample gives the types short names, CoachingContext and CoachingEvaluation, so the rest of the code stays readable.
2. Ask for permission at a calm moment
The app needs the person's permission, and when you ask matters more than how. Ask from a deliberate action, such as a settings row, after saying what it does. Never ask in the middle of a transfer: if a scammer is on the phone, they will tell the person to tap Not Now.
static func requestAuthorization(for context: CoachingContext) async -> Bool {
let status = try? await InsightEvaluator().requestAuthorization(for: context)
return status == .authorized
}
authorizationStatus(for:) has five answers. .authorized means go ahead. .notDetermined and .deniedRequestable mean you may ask, from that calm moment. .denied and .unavailable mean you may not; the person can still turn it on in Settings > Privacy & Security > Impersonation Risk Detection.
3. Start the evaluation early
The result is the return value of requestEvaluation(context:). There is no delegate, notification or extension. The session warns that it can take a couple of seconds and needs internet access, so do not wait for the Confirm button to start it. The sample starts it as soon as the transfer screen appears, and the seconds pass while the person reads the screen:
private func startEvaluation() {
let context = TrustInsightsCheck.context()
pending = Task { await TrustInsightsCheck.evaluate(context) }
}
evaluate checks the authorization status, calls requestEvaluation, and turns every failure into nil, which means "no evaluation, use your existing checks". A failure must never block a transfer that your app would otherwise allow. These are the errors, and what the sample does with each:
| EvaluationError | What it means | What to do |
|---|---|---|
contextInvalid | The request is malformed | A bug; fix it |
denied | Permission was withdrawn | Carry on without it |
notAvailable | Trust Insights is not available | Carry on without it |
rateLimitExceeded | Too many requests | Check that every evaluation is reported (step 4) |
serverError, localError | A transient failure | Try again on the next transfer, not in a loop |
payloadSecurityError | The payload could not be secured | Carry on without it, and log it |
A production app should also stop waiting after a deadline of its own and go ahead on its usual checks. If you do that, the evaluation that turns up after the deadline still has to be reported, as .notUsedError.
4. Decide, and report what you decided
Every evaluation has to be reported with reportConsumption, exactly once. The session is plain about it: the call is mandatory, and an app that skips it may be rate-limited. The report says what the app did with the result, and it is how Apple learns how the insight is being used.
The sample puts the report on every branch, where you can see it:
static func decide(_ evaluation: CoachingEvaluation?) -> Friction {
// No evaluation: no permission, no network, or an error. The app's own checks decide, as before.
guard let evaluation else { return .none }
switch evaluation.insight.outcome {
case .success(.unknown):
// No evidence of coaching. That is not evidence of safety, so friction stays as it was.
evaluation.reportConsumption(.usedUnchangedFriction, insightsUsed: [evaluation.insight])
return .none
case .success(.medium):
evaluation.reportConsumption(.usedIncreasedFriction, insightsUsed: [evaluation.insight])
return .warn
case .success(.high):
evaluation.reportConsumption(.usedIncreasedFriction, insightsUsed: [evaluation.insight])
return .hold
case .success:
// A level newer than this sample. Do not guess what it means.
evaluation.reportConsumption(.notUsedError)
return .none
case .failure(let error):
// The evaluation came back, but the insight inside it failed. It still has to be reported.
log.notice("Insight failed: \(String(describing: error), privacy: .public)")
evaluation.reportConsumption(.notUsedError)
return .none
}
}
Two branches are easy to miss. The .failure case is an evaluation that came back with a failed insight inside it, and it still counts as an evaluation. And a transfer the person cancels before confirming still had an evaluation, which the sample reports as .notUsedNotNeeded.
What the warning says matters as much as when it appears. The sample's medium warning asks whether someone is telling the person what to do, and says the bank will never ask them to move money to keep it safe. Its high response pauses the transfer and promises a call back on the number the bank already has. The Swift Dev's design guide goes much further into this than I do here, and it is worth reading before you write your own copy.
5. Only add friction, and keep a record
A decision made in the app can be changed by anyone who can change the app. For a warning, that does not matter. A changed app that fakes unknown gets the transfer the bank would have allowed before Trust Insights. So the sample only ever adds friction: unknown leaves the transfer as it was, and nothing skips a check.
Apple's documentation shows the other direction too: its example uses a result with no sign of coaching to skip one of the app's checks, and .usedReducedFriction exists for that. That needs a result your server can trust. The evaluation carries a signedPayload for server-side validation, but as of November 2026 what a server needs to check it is not public. The sample does not use it, and neither does this article.
Last, the sample logs the result and the friction with the transfer. A real app keeps them in its own records, because the next section is about what the fraud team does with them.
What you cannot test, and whose job that is
I ran the sample on a device, and the whole flow works: permission, the evaluation, the report. What I could not do is make it see a scam. Apple does not say what it treats as coaching, so there is no way to set up the conditions for a medium or a high on purpose. The session says values and errors can be overridden with launch arguments in the scheme, and refers to the documentation for the list. The documentation does not have the list yet. Until it does, the branches for medium and high can only be tested by reading them.
That leaves the judgement with the banks, which is where it belongs anyway. How much weight a medium gets is a decision for your fraud team, made from your own numbers: what happened to the transfers that got each value, and how many of the unknowns turned out to be fraud. Where the results do not match what your fraud team sees, tell Apple through Feedback Assistant. The model gets better when the banks using it say where it is wrong.
Start
If you work on a bank, brokerage, payments or crypto app, put this on the screen where money leaves the account. If you work on any other app where money, an account or remote access changes hands, put it there. It is all app code, it fits in one file, and the sample is there to copy from. Every app that adopts it leaves scammers one less place to send their victims.
Further reading
Anton Gubarenko's iOS 27: Trust Insights Framework is a longer walk through the API, with more on the kinds of apps outside banking that can use it. The Swift Dev's Trust Insights: Detect Coerced Actions In Sensitive iOS Flows is the one to read for the design of the warnings themselves.
Sources
- Trust Insights, Apple's documentation
InsightEvaluationandsignedPayloadIsLikelyBeingCoachedInsight- Meet Trust Insights, WWDC26
- TrustInsightsSample, the sample project
- iOS 27: Trust Insights Framework, Anton Gubarenko
- Trust Insights: Detect Coerced Actions In Sensitive iOS Flows, The Swift Dev