KYC onboarding
A fintech runs its own onboarding screens and wants one call that says verified or not, and why. This recipe uses the KYC capability with the Python SDK; the JavaScript SDK has the same methods in camelCase.
from ward import Ward
client = Ward()
def start(applicant):
e = client.kyc.send_code("email", applicant["email"], locale="en")
p = client.kyc.send_code("sms", applicant["phone"], locale="en")
return {"email": e.challenge_id, "phone": p.challenge_id} # keep in the onboarding session
def confirm(challenges, email_code, sms_code):
ok_e = client.kyc.confirm_code(challenges["email"], email_code).verified
ok_p = client.kyc.confirm_code(challenges["phone"], sms_code).verified
return ok_e and ok_p
def decide(applicant, challenges):
r = client.kyc.verify(applicant, email_challenge_id=challenges["email"],
phone_challenge_id=challenges["phone"], locale="en",
external_ref=applicant["id"])
if r.status == "VERIFIED":
open_account(applicant, verification_id=r.verification_id)
elif r.status == "REVIEW":
queue_for_compliance(r.verification_id, [x["detail"] for x in r.reasons])
tell_applicant("We're reviewing your application.")
else:
tell_applicant(r.reason) # user_message: safe to show
return r
Handling reasons in the UI
Map the reasons the applicant can fix to the field that needs fixing (field: email, phone, document) and show user_message next to it:
| Reason | UI action |
|---|---|
EMAIL_DOMAIN_TYPO |
Pre-fill the suggested address |
EMAIL_DISPOSABLE, EMAIL_ROLE_ACCOUNT |
Ask for a personal email |
PHONE_VOIP, PHONE_NOT_PERSONAL, PHONE_NOT_SMS_CAPABLE |
Ask for a mobile number |
*_NOT_CONFIRMED |
Resend the code |
SANCTIONS_*, PEP_MATCH, CONTACT_* |
Show the generic message only; route to compliance |
Close the loop
Months later, when an account turns out fraudulent or clean, report it:
client.kyc.outcome(r.verification_id, "synthetic_identity")
Outcomes build your account's contact history and train its contact-risk model.