Policies: how a rule becomes an action
Your policy turns reason codes into an action. Your fraud team owns the rules, the limits, the queues and the messages, per journey and market, and every version is kept, so any decision can be traced to the rule that applied at the time.
Names and fields are illustrative and agreed in the sandbox before any contract.
On this page
How a policy decides
- Rules read events and reason codes.
A rule names the events it applies to and a condition on the session’s signals, so it always says why it fired. There is no threshold on a score to explain later.
- The strictest action wins.
Every matching rule is recorded with the decision, and the action is the strictest of them: allow, warn, step up, cool off, hold, block. A warning never hides a hold.
- Per journey and market.
A retail payment in India and a merchant payout in Singapore run different policies, with each market’s cool-off and limits. One SDK, one decision shape, your rules.
- Versioned.
A change is a new version, and each decision names the version that made it. You can show which rule applied, to whom, and when it changed.
A policy file
The retail policy, version 14: eight rules and a default. It is the policy behind every decision on this site: the emulator blocked at sign-up, the face check on a new phone, the new payee held to ₹10,000 for 12 hours, the coached payment held and queued for a call back.
The policy
retail.yaml
# retail.yaml · version 14
# Every matching rule is recorded.
# The strictest action wins.
- id: block_emulator
on: any
when: device.emulator or device.tampered
then: block
message: session_unverified
- id: hold_scam_pattern
on: [payee.add, payment.initiate]
when: payee.mule_linked or (call.active and screen.shared)
then: hold
queue: scam-callback
message: payment_paused_callback
- id: step_up_takeover
on: [login.attempt, device.change, payee.add, payment.initiate]
when: sim.moved or behaviour.drift
then: step_up
message: face_check
- id: step_up_remote_access
on: [signup.start, login.attempt, device.change]
when: screen.shared or remote.access
then: step_up
message: screen_share_stop
- id: step_up_number_unverified
on: [signup.start, login.attempt, device.change]
when: not number.verified
then: step_up
message: code_fallback
- id: cool_off_new_device
on: [login.attempt, payee.add, payment.initiate]
when: device.new
then: cool_off
hours: 12
message: new_device_limit
- id: cool_off_rebind
on: [device.change]
when: always
then: cool_off
hours: 12
message: rebind_limit
- id: warn_call
on: [payee.add, payment.initiate]
when: call.active
then: warn
message: call_warning
- id: default
then: allowWhat a condition names
A condition names a signal the SDK reads. When the rule fires, the signal is recorded as its reason code, so the rule and the reason are one record. The names below are the ones the file above uses.
| Signal | Recorded as | Read by |
|---|---|---|
| device.emulator | EMULATOR | Device intelligence |
| device.tampered | DEVICE_TAMPERED | Device intelligence |
| screen.shared | SCREEN_SHARED | Device intelligence |
| remote.access | REMOTE_ACCESS | Device intelligence |
| device.new | NEW_DEVICE | SIM and device binding |
| sim.moved | SIM_MOVED | SIM and device binding |
| number.verified | NUMBER_VERIFIED | Silent network verification |
| behaviour.drift | BEHAVIOUR_DRIFT | Behavioural biometrics |
| call.active | ACTIVE_CALL | Scam signals |
| payee.mule_linked | PAYEE_MULE_LINKED | Payee and mule graph |
Versions
A change is a new version, and the version number travels with every decision it makes. Version 14 made two changes to version 13: coached payments go to the call-back queue, with the message the customer reads named in the rule, and a new device is limited for 12 hours instead of 24.
Version 13 to 14
retail.yaml
--- retail.yaml · version 13
+++ retail.yaml · version 14
- id: hold_scam_pattern
on: [payee.add, payment.initiate]
when: payee.mule_linked
or (call.active and screen.shared)
then: hold
- queue: fraud-review
+ queue: scam-callback
+ message: payment_paused_callback
- id: cool_off_new_device
on: [login.attempt, payee.add, payment.initiate]
when: device.new
then: cool_off
- hours: 24
+ hours: 12
message: new_device_limitBecause the decision object carries policy.version, a decision made under version 13 still reads as one: the 24-hour limit it applied is the limit that was in force, not an error.
Messages and queues
A rule can name the message the customer sees and the queue a person works. Messages are written for customers, not analysts: what happened to the money and what happens next. Queues are the names your FRM or case manager already uses; DeepID opens nothing of its own.
| Key | Action | The customer reads |
|---|---|---|
| signup_verified | allow | You’re verified. No code was needed. |
| login_ok | allow | Welcome back. Nothing to type. |
| payee_added | allow | R. Sharma is now one of your payees. |
| payment_sent | allow | Payment sent. |
| call_warning | warn | If someone on a call asked you to make this payment, stop. Banks, police and courts never ask you to move money to keep it safe. |
| first_payee_warning | warn | This payee is new to you. Check the name before you send. |
| face_check | step_up | Confirm it’s you with a quick face check. |
| screen_share_stop | step_up | Someone may be able to see your screen. Stop sharing it, then confirm it’s you. |
| code_fallback | step_up | We couldn’t confirm your number with your network. Enter the code we’ve sent by SMS. |
| new_device_limit | cool_off | You can send up to ₹10,000 from this phone now. You get the full limit in 12 hours. |
| new_payee_limit | cool_off | You can send up to ₹10,000 to this payee now. You get the full limit in 12 hours. |
| rebind_limit | cool_off | Your new phone is set up. You can send up to ₹10,000 now, and the full limit in 12 hours. |
| payment_paused_callback | hold | Payment paused. Your money hasn’t left your account. We’ll call you on +91 •••• 3210. |
| payee_paused_callback | hold | Payee not added yet. We’ll call you on +91 •••• 3210 before anything changes. |
| session_unverified | block | We couldn’t verify this session. Your money is safe. |
Map any key to your own copy and languages. Whatever is shown is logged with the decision, as the decision object page describes.
Journeys
The same policy sets the checks at each step of a journey, their order and their fallbacks: which login route runs first, when a face check steps in, how long a cool-off lasts in each market. The journey builder changes them without an app release, and each change is a new version of the policy the decision names.
Wire one moment in the sandbox, before any contract.
Sign-up or login end to end, with the decision back in the app and as an event, so your engineers see the shape of it before anyone signs anything.