Skip to content

SmartOTP Overview ​

SmartOTP is CAKE's soft-token service: a partner app registers a device, activates it with a client secret key, and then generates TOTP codes offline. Codes come in two strengths — basic OTP derived from the secret and time, and advance OTP additionally bound to a specific transaction through a challenge key.

INFO

Every sample in this documentation uses the same fictional identity — Nguyễn Văn A, NID 001xxxxxxxxx, user_id user_00000001, ref_id ref_00000001. Credentials, tokens and keys appear as <PLACEHOLDER>. None of it is real data; replace it all with your own values.

Prerequisites ​

WhatDescription
Partner IDIdentifies your organization. Sent in every request body as partner_id. Provided by CAKE.
Partner KeyThe secret paired with your Partner ID. Sent in every request body as partner_key. Provided by CAKE.
Base URLIssued by CAKE per partner, in the form https://<HOSTNAME>/api/v1.
Mobile SDKThe SOTP SDK embedded in your app holds the secret, validates the PIN, and generates codes.
Firebase push tokenRequired at activation so the server can push challenge keys to the device.

WARNING

Every request must use https, never http — the partner_key travels in the request body, so a plaintext request discloses your partner credential.

Samples throughout this documentation write the base URL as <BASE_URL> — substitute the value above.

Core Concepts ​

Key Derivation ​

FieldTypeDescription
Client secret keystringHashed from user_id, device_id and registration_id (unique_id) with the server's private key. Returned by Activate as key.
Challenge keystringHashed from user_id, device_id, registration_id, app_id, transaction_id, transaction_amount and unique_id with the server's private key. Returned by Create Transaction.

OTP Types ​

FieldTypeDescription
basicstringGenerated from TOTP using the client secret key and the current time. Not bound to any transaction.
advancestringGenerated from TOTP using the client secret key, the challenge key and the current time. Bound to one specific transaction.

Advance OTP is what makes the code non-transferable: because the challenge key encodes the transaction amount and ID, a code captured for one transfer cannot authorize a different one.

TOTP parameters ​

Returned by Activate and Sync Time:

FieldTypeDescription
algorithmstringHmacSHA512.
periodintSeconds a code stays valid — 15.
lengthintNumber of digits — 6.
typestringtotp.
issuerstringSOTP.

A 15-second period is much shorter than the 30-second default most authenticator apps assume. Clock drift matters — call Sync Time when validation starts failing.

Authentication ​

The API is JSON-based and authorized by partner_id and partner_key sent in the body of each call, not in a header.

http
POST /api/v1/saas-sotp/get-status HTTP/1.1
Host: <HOSTNAME>
Content-Type: application/json
Accept: application/json
Accept-Charset: utf-8

partner_id={{partner_id}}&partner_key={{partner_key}}&token={{token}}

Most requests also require the device's token, generated by Register. The two exceptions are Register itself and Activate.

WARNING

partner_id and partner_key are backend credentials and must never ship inside your mobile app. The device-scoped token is what the app is allowed to hold. Proxy every SmartOTP call through your own Partner API.

How It Works ​

sequenceDiagram
    autonumber
    participant User
    participant App as Mobile App + SDK
    participant PA as Partner API
    participant SOTP as Soft-OTP API

    Note over App,SOTP: Phase 1 — Register and activate
    App->>PA: Begin Smart OTP setup
    PA->>SOTP: POST /register
    SOTP-->>PA: registration_id, activation_key, token
    PA->>SOTP: POST /activate (+ push_token)
    SOTP-->>PA: key, period, algorithm, length
    PA-->>App: TOTP config + secret
    Note over App: SDK stores secret behind the user's PIN

    Note over User,SOTP: Phase 2 — Advance OTP for a transaction
    User->>App: Enter amount, receiver, description
    App->>PA: Create transaction
    PA->>SOTP: POST /create-transaction
    SOTP-->>PA: challenge_key
    SOTP-->>App: challenge_key pushed via Firebase
    PA-->>App: transaction_id, otp_type
    User->>App: Enter PIN
    App->>App: isValidPin, then generateSmartOTP(pin, challenge_key)
    User->>App: Confirm transaction
    App->>PA: transaction_id + OTP
    PA->>SOTP: POST /validate-sotp (otp_type advance)
    SOTP-->>PA: code 1 Success
    PA-->>App: Approved

Phase 1 — Register and activate ​

  1. Your Partner API calls Register, which returns registration_id, a one-time activation_key, and the device token.
  2. It then calls Activate with the activation_key and the device's Firebase push_token. The response carries the client secret key and the TOTP configuration.
  3. The SDK stores the secret on the device, protected by the user's PIN.

Phase 2 — Basic OTP ​

For a login or a low-risk action, the app validates the PIN and generates a code with no transaction context:

kotlin
SdkSoft.isValidPin(pin = "pin")           // false → show an error, ask again
val otp = SdkSoft.generateSmartOTP(pin = "PIN")

Your Partner API then calls Validate OTP with otp_type: "basic".

Phase 3 — Advance OTP ​

  1. The user enters the transaction details and your Partner API records the transaction.
  2. Your Partner API calls Create Transaction. The Soft-OTP API generates the challenge_key, returns it, and pushes it to the device via Firebase.
  3. Your Partner API returns transaction_id and otp_type to the app — plus the challenge_key itself when push notifications are switched off.
  4. The app validates the PIN and generates the bound code:
kotlin
SdkSoft.isValidPin(pin = "pin")
val otp = SdkSoft.generateSmartOTP(pin = "PIN", challenge_key = "fake_challenge_key")
  1. The user confirms; the app sends transaction_id and the OTP to your Partner API.
  2. Your Partner API calls Validate OTP with otp_type: "advance" and the transaction_id, then converts the result into its own status code for the app.

Phase 4 — Sync time ​

TOTP depends on the device clock. When the user opens the app, the SDK can call Sync Time through your Partner API to check period and client_timestamp against the server and return the server timestamp.

Shared Response Envelope ​

json
{
  "success": true,
  "code": 1,
  "message": "Success",
  "data": {}
}
FieldTypeDescription
successboolRequest status.
codeintThe status code. 1 means success — not 0 as in CAKE's eKYC, NFC and FaceAuthen APIs.
messagestringThe status or error message.
dataobjectEndpoint-specific payload. Some endpoints return fields at the top level instead.

WARNING

Success on this API is code: 1. If you already integrate CAKE eKYC, NFC or FaceAuthen, note those use code: 0 for success — reusing a shared response handler across both will misread every SmartOTP result.

Error Codes ​

Messages are returned in Vietnamese, reproduced verbatim below.

CodeMessage
1Success (OTP hợp lệ)
3Parameter invalid
103Thông tin Partner không chính xác. — Partner details are incorrect.
121Yêu cầu vượt giới hạn. Vui lòng thử lại sau ít phút. — Request limit exceeded.
123Yêu cầu vượt giới hạn. Vui lòng thử lại sau ít phút. — Request limit exceeded.
124OTP không hợp lệ. — Invalid OTP.
125OTP hết hạn sử dụng. — OTP expired.
126Có lỗi xảy ra vui lòng thử lại. — An error occurred, please retry.
129Token không hợp lệ. — Invalid token.
190001Thông tin không hợp lệ, device_id đã kích hoạt Soft-OTP. — Device already activated.
190005SmartOTP đang kích hoạt trên một thiết bị khác. — SmartOTP is active on another device.
190108Tài khoản chưa hoạt động. — Account not activated.
190201Không thể tạo transaction hoặc transaction_id đã tồn tại. — Cannot create transaction, or the ID already exists.
190203Không tìm thấy transaction. — Transaction not found.
190301Period không chính xác. — Incorrect period.
190303Client time không chính xác. — Incorrect client time.

Next Steps ​

Developers