MarketHK · Phase 4
Customer TOTP API
Replace expensive SMS OTPs with standard Authenticator apps (Google / Microsoft / 1Password). Enroll a contact once, then challenge and verify with a workspace API key.
Auth
- In the workspace: TOTP → API → create an API key (scope
totp). - Send
Authorization: Bearer mk_live_…(orX-Api-Key).
Endpoints
GET /v1/totp
GET /v1/totp?contactId=…
POST /v1/totp/enroll
POST /v1/totp/confirm
POST /v1/totp/challenge
POST /v1/totp/verify
POST /v1/totp/disable
# Same actions also work as:
POST /v1/totp { "action": "challenge", ... }Enroll + confirm
curl -s -X POST https://YOUR_HOST/v1/totp/enroll \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{"contactId":"CONTACT_UUID","externalUserId":"bank-user-123","issuer":"YourBank"}'
curl -s -X POST https://YOUR_HOST/v1/totp/confirm \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{"contactId":"CONTACT_UUID","code":"123456"}'Challenge + verify
curl -s -X POST https://YOUR_HOST/v1/totp/challenge \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{"contactId":"CONTACT_UUID","purpose":"login"}'
curl -s -X POST https://YOUR_HOST/v1/totp/verify \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{"challengeId":"CHALLENGE_UUID","code":"123456"}'Hybrid policy (admin-owned)
Default OTP policy is customer_totp → sms, configured by a platform admin for each workspace. Workspace users only use Direct send; they do not pick routing policies. Customer apps call /v1/totp/challenge then /v1/totp/verify; SMS runs only if TOTP is not enrolled or verification fails (per admin policy).
SDK sketch
export async function challengeTotp(apiKey, contactId) {
const res = await fetch("/v1/totp/challenge", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ contactId, purpose: "transfer" }),
});
return res.json();
}
export async function verifyTotp(apiKey, challengeId, code) {
const res = await fetch("/v1/totp/verify", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ challengeId, code }),
});
return res.json();
}Manage TOTP and API keys · Enroll contacts · Book consultation