Skip to content

Quickstart: a first decision in six steps

Install the SDK, start it when the app opens, call it when the customer confirms a payment and branch on the action. Then receive the same decision in your backend. The examples use the web SDK; Android, iOS, React Native and Flutter follow the same three steps.

Names and fields are illustrative and agreed in the sandbox before any contract.

On this page

1. Request sandbox access

Sandbox access comes before any contract. Request it and name the journey you want to protect first. Access for your platforms, the event names and fields you will build against, and the questions your security team will want settled before a pilot arrive with it.

2. Install the SDK

One package per platform. The web SDK and the Android SDK are shown; the SDKs page has iOS, React Native and Flutter.

Install

Terminal

npm install @deepid/web

Install

build.gradle.kts · Android

dependencies {
  implementation(
    "com.deepidsdk:deepid-android:<version>"
  )
}

3. Start it once

Start the SDK as the app opens, so it has read the session before the first call. From then on it reads the network and SIM, the device and its integrity, the owner’s rhythm, an active call and a shared screen. Your app passes none of this.

Start once

app.ts

import { createDeepID } from "@deepid/web"

// Once, as the page loads
const deepid = createDeepID({ key: DEEPID_SANDBOX_KEY })

4. Call at the payment

Call decide when the customer confirms the payment, before you send it to the rail. Pass your own identifiers and the payment’s details; the SDK adds what it has read. Ananya’s payment: ₹96,000 over UPI to a payee added 3 minutes ago.

The call

payment.ts

const decision = await deepid.decide(
  "payment.initiate",
  {
    customer: user.id,
    payee: payee.vpa,
    amount: { value: 96000, currency: "INR" },
    rail: "UPI",
  },
)

if (decision.action === "allow") sendPayment()
else show(decision.message)

5. Handle all six actions

Six actions, in ladder order. Show decision.message.text as it is, or map decision.message.key to your own copy and languages. Handle every action explicitly, so a new policy rule never falls through to a default you did not intend.

The six actions: when each applies and what the customer sees
ActionWhenThe customer sees, for example
allowEverything holds.Payment sent.
warnSomething is unusual, but the customer is in control.This payee is new to you. Check the name before you send.
step_upIdentity is uncertain.Confirm it’s you with a quick face check.
cool_offA new device, SIM or payee.You can send up to the starter limit now. You get the full limit in 12 hours.
holdSigns of coercion or a mule payee.Payment paused. Your money hasn’t left your account. We’ll call you on +91 •••• 3210.
blockClear attack.We couldn’t verify this session. Your money is safe.

PaymentViewModel.kt

// The SDK has read the session since launch.
val decision = DeepID.decide(
  Event.PaymentInitiate(
    customer = user.id,
    payee = payee.vpa,
    amount = Money(96_000, "INR"),
    rail = "UPI",
  )
)

when (decision.action) {
  Action.ALLOW    -> sendPayment()
  Action.WARN     -> showWarning(decision.message)
  Action.STEP_UP  -> stepUp(decision.message)
  Action.COOL_OFF -> applyLimit(decision.message)
  Action.HOLD     -> showPaused(decision.message)
  Action.BLOCK    -> endSession(decision.message)
}

6. Receive the event

The same decision reaches your backend as an event, so a payment service acts on what DeepID sent rather than on what the app reports. The decision id is the key: a repeated delivery is stored once. See Webhooks.

Your backend

hooks.ts

// Act on what DeepID sent, not on what the app
// reports. The decision id is the key: a repeated
// delivery is stored once and opens no second case.
app.post("/hooks/deepid", async (req, res) => {
  const { type, data } = req.body
  if (type !== "decision.created") {
    return res.sendStatus(200)
  }

  const fresh = await decisions.put(
    data.decision_id,
    data,
  )
  if (fresh && data.queue) {
    await cases.open(data.queue, data)
  }

  res.sendStatus(200)
})

That is the first moment, end to end. Try one-tap login on your own number to see the login moment before you wire it.

Integrating each moment

Start with the moment your first journey covers. Each later moment is one more call from the same SDK, read against everything bound and read before it, and its reasons join the same decision shape.

Sign-up signup.start

The real person, bound to their own phone and SIM.

  1. 1

    Start the SDK when sign-up opens

    The session begins with the first screen, so the device and its integrity are read from the start.

  2. 2

    Verify the identity

    Aadhaar through DigiLocker or offline KYC, PAN and other ids, checked at the source, with documents or video KYC where your journey needs them.

  3. 3

    Take the selfie

    Liveness gives the customer an instant answer on the phone, and catches photos, masks, deepfakes and injected streams.

  4. 4

    Confirm on your server

    Your server asks DeepID for the final verdict before the account opens. When in doubt it fails closed: deny or review, never pass.

  5. 5

    Bind the phone and SIM

    The account is bound to this device and its SIM, so every later moment has something to check against.

The call

signup.start.ts

const decision = await deepid.decide(
  "signup.start",
  {
    phone: form.phone,
    journey: "retail",
  },
)

if (decision.action === "allow") openAccount()
else show(decision.message)

Returns: Allow, step up or block, with its reasons, to the app and as an event to your FRM. Reads with Identity and eKYC, Document and ID verification, Liveness and face match, AML screening, Bank account verification, Silent network verification, SIM and device binding, Device intelligence and Payee and mule graph.

Login login.attempt

One tap, and the SIM bound at sign-up is checked.

  1. 1

    Ask for the number

    The network check starts as soon as the number is typed, so it is often done before the customer taps Continue.

  2. 2

    Let the routes run in order

    The network check, the SIM check, Aadhaar consent, then a one-time code. The first that verifies ends it, or your policy requires all of them.

  3. 3

    Pass the token to your server

    The app receives a single-use token. A device refused by the risk gate gets none.

  4. 4

    Redeem it before signing in

    Your server redeems the token with DeepID, server to server, and only then signs the customer in.

  5. 5

    Expect the bound phone and SIM

    The phone and SIM bound at sign-up are checked here. A change hands over to the new phone or SIM moment.

In the app

login.ts

// In the app: the routes run in your policy's order
// (network, SIM, Aadhaar consent, then a one-time code).
// The first that verifies ends it.
const { token } = await deepid.login({
  phone: input.number,
})
await api.post("/session", { token })

On your server

session.ts

// On your server: redeem the single-use token with
// DeepID, server to server, before signing anyone in.
const result = await deepidServer.redeem(token)

if (result.verified) return signIn(result.customer)
return reject()

Returns: Allow, or a step-up when the binding or the device does not hold, with its reasons. Reads with Silent network verification, SIM and device binding, Device intelligence and Behavioural biometrics.

New phone or SIM device.change

The face from sign-up, checked before anything is bound again.

  1. 1

    Notice the change

    The SDK sees a new device, or a SIM that moved to another phone, when the app opens or at login.

  2. 2

    Call before money can move

    Call DeepID before the new phone can add a payee or pay.

  3. 3

    Step up with a face check

    The live face is checked against the one matched at sign-up.

  4. 4

    Re-bind the customer

    The account is bound to the new phone and SIM only after the customer proves it is them.

  5. 5

    Cool off

    What can leave the account is limited for as long as your policy and market require: ₹10,000 for 12 hours in the story.

The call

device.change.ts

const decision = await deepid.decide(
  "device.change",
  { customer: user.id },
)

if (decision.action === "allow") bindDevice()
else show(decision.message)

Returns: Usually a step-up, then a cool-off, with its reasons. Reads with Silent network verification, SIM and device binding, Liveness and face match and Device intelligence.

New payee payee.add

The payee scored, from the phone and SIM already bound.

  1. 1

    Call before the payee is saved

    When the customer confirms the payee’s details.

  2. 2

    DeepID reads the session

    An active call or a shared screen, details pasted or dictated, the device, and the payee’s links in the mule graph.

  3. 3

    Act on the decision

    Allow, warn, cool off or hold, and show the message written for the customer.

  4. 4

    Your FRM gets the same decision

    As an event with its reasons, so your rules can use it.

The call

payee.add.ts

const decision = await deepid.decide(
  "payee.add",
  {
    customer: user.id,
    payee: payee.vpa,
  },
)

if (decision.action === "allow") savePayee()
else show(decision.message)

Returns: Allow, warn, cool off or hold, with its reasons. Reads with Device intelligence, Behavioural biometrics, Scam signals and Payee and mule graph.

Payment payment.initiate

The SIM bound at login is the SIM expected at payment.

  1. 1

    Call when the customer confirms

    Before your app sends the payment instruction to the rail.

  2. 2

    DeepID reads the session around it

    From login to this screen: the call, the shared screen, the typing and the payee, against everything read before.

  3. 3

    Act on the action

    Show the message it carries. A held payment comes with a call back on the registered number, +91 •••• 3210 in the story.

  4. 4

    The same decision reaches your FRM

    With its reason codes, logged for audit.

The call

payment.initiate.ts

const decision = await deepid.decide(
  "payment.initiate",
  {
    customer: user.id,
    payee: payee.vpa,
    amount: { value: 96000, currency: "INR" },
    rail: "UPI",
  },
)

if (decision.action === "allow") sendPayment()
else show(decision.message)

Returns: One of six actions, from allow to block, with its reasons. Reads with Device intelligence, Behavioural biometrics, Scam signals and Payee and mule graph.

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.