From 2f65958746099cffce67da9850578dfa694c58b1 Mon Sep 17 00:00:00 2001 From: Raghu Kumar Date: Thu, 27 Aug 2026 14:05:00 -0700 Subject: [PATCH 1/4] NEB-4781: Add KYC, geo, and bot-detection structured fields --- HISTORY | 3 + README.md | 71 ++++++++++++++++++++++ lib/sift/version.rb | 2 +- spec/unit/client_structured_fields_spec.rb | 60 ++++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 spec/unit/client_structured_fields_spec.rb diff --git a/HISTORY b/HISTORY index 4c6d5e8..98d0b6e 100644 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,6 @@ +=== 4.7.0 2026-08-27 +- Added support for KYC, geo, and bot-detection structured fields ($nationality, $year_of_birth, $kyc, $geo, $bot_identification) on existing event types + === 4.6.0 2026-02-10 - Bump the minimum version of httparty to 0.23.3 to ensure protection against CVE-2025-68696 - Refactor Client to use dedicated internal HTTP clients for different API endpoints diff --git a/README.md b/README.md index 1b92c2d..2dcaf03 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,77 @@ response.api_error_message # Error message associated with status Error Code response = client.score(user_id) ``` +### New KYC Signals, Geo & Bot Detection + +```ruby +# $verification event with KYC outcome +response = client.track("$verification", { + "$user_id" => "23056", + "$verification_type" => "$kyc", + "$status" => "$success", + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$basic", + "$bin_nationality_match" => false, + "$provider" => "lexisnexis" + } +}) + +# $create_account with account-level identity attributes +response = client.track("$create_account", { + "$user_id" => "23056", + "$nationality" => "US", + "$year_of_birth" => 1985, + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$full", + "$provider" => "prove" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + } +}) + +# $login event with geo and bot-detection signals +response = client.track("$login", { + "$user_id" => "23056", + "$login_status" => "$success", + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + } +}) + +# $transaction event with KYC, geo, and bot-detection signals +response = client.track("$transaction", { + "$user_id" => "23056", + "$amount" => 15230000, + "$currency_code" => "USD", + "$kyc" => { + "$names_match" => true, + "$bin_nationality_match" => true, + "$provider" => "lexisnexis" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$suspected", + "$provider" => "human_security" + } +}) +``` + ## Decisions To learn more about the decisions endpoint visit our [developer docs](https://sift.com/developers/docs/ruby/decisions-api/get-decisions). diff --git a/lib/sift/version.rb b/lib/sift/version.rb index 9f7968d..29a1d22 100644 --- a/lib/sift/version.rb +++ b/lib/sift/version.rb @@ -1,5 +1,5 @@ module Sift - VERSION = "4.6.0" + VERSION = "4.7.0" API_VERSION = "205" VERIFICATION_API_VERSION = "1.1" end diff --git a/spec/unit/client_structured_fields_spec.rb b/spec/unit/client_structured_fields_spec.rb new file mode 100644 index 0000000..7f6f72a --- /dev/null +++ b/spec/unit/client_structured_fields_spec.rb @@ -0,0 +1,60 @@ +require_relative '../spec_helper' +require 'sift' + +describe Sift::Client do + + before :each do + Sift.api_key = nil + end + + it "Successfully submits a $create_account event with KYC, geo, and bot-detection structured fields" do + response_json = { :status => 0, :error_message => "OK" } + stub_request(:post, "https://api.siftscience.com/v205/events"). + with { |request| + parsed_body = JSON.parse(request.body) + expect(parsed_body["$nationality"]).to eq("US") + expect(parsed_body["$year_of_birth"]).to eq(1985) + expect(parsed_body["$kyc"]).to eq({ + "$names_match" => true, + "$kyc_level" => "$basic", + "$bin_nationality_match" => true, + "$provider" => "lexisnexis" + }) + expect(parsed_body["$geo"]).to eq({ + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }) + expect(parsed_body["$bot_identification"]).to eq({ + "$result" => "$human", + "$provider" => "datadome" + }) + }.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {}) + + api_key = "foobar" + properties = { + :$type => "$create_account", + :$user_id => "23056", + :$nationality => "US", + :$year_of_birth => 1985, + :$kyc => { + :$names_match => true, + :$kyc_level => "$basic", + :$bin_nationality_match => true, + :$provider => "lexisnexis" + }, + :$geo => { + :$uuid => "gc-abc-123", + :$provider => "geocomply" + }, + :$bot_identification => { + :$result => "$human", + :$provider => "datadome" + } + } + + response = Sift::Client.new(:api_key => api_key, :version => "205") + .track("$create_account", properties) + expect(response.ok?).to eq(true) + expect(response.api_status).to eq(0) + end +end From 76b72baa64a6e90a08c878ce0fca466a317db20d Mon Sep 17 00:00:00 2001 From: Raghu Kumar Date: Wed, 2 Sep 2026 12:22:40 -0700 Subject: [PATCH 2/4] NEB-4781: Add structured fields to integration test app --- .../events_api/test_events_api.rb | 121 ++++++++++++++++-- 1 file changed, 109 insertions(+), 12 deletions(-) diff --git a/test_integration_app/events_api/test_events_api.rb b/test_integration_app/events_api/test_events_api.rb index 299b39d..68e6f79 100644 --- a/test_integration_app/events_api/test_events_api.rb +++ b/test_integration_app/events_api/test_events_api.rb @@ -24,7 +24,17 @@ def login() "$brand_name" => "sift", "$site_domain" => "sift.com", "$site_country" => "US", - + + # Structured fields (geo / bot detection) + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + }, + # Send this information with a login from a BROWSER client. "$browser" => { "$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", @@ -107,13 +117,28 @@ def transaction() # For marketplaces, use $seller_user_id to identify the seller "$seller_user_id" => "slinkys_emporium", - + + # Structured fields (KYC / geo / bot detection) + "$kyc" => { + "$names_match" => true, + "$bin_nationality_match" => false, + "$provider" => "prove" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "human_security" + }, + # Sample Custom Fields "digital_wallet" => "apple_pay", # "google_wallet", etc. "coupon_code" => "dollarMadness", "shipping_choice" => "FedEx Ground Courier", "is_first_time_buyer" => false, - + # Send this information from a BROWSER client. "$browser" => { "$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", @@ -121,7 +146,7 @@ def transaction() "$content_language" => "en-GB" } } - + return @@client.track("$transaction", properties, :warnings => 'true') end @@ -233,7 +258,22 @@ def create_order() "coupon_code" => "dollarMadness", "shipping_choice" => "FedEx Ground Courier", "is_first_time_buyer" => false, - + + # Structured fields (KYC / geo / bot detection) + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$basic", + "$provider" => "lexisnexis" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + }, + # Send this information from a BROWSER client. "$browser" => { "$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", @@ -241,7 +281,7 @@ def create_order() "$content_language" => "en-GB" } } - + return @@client.track("$create_order", properties) end @@ -457,7 +497,24 @@ def update_account() "$social_sign_on_type" => "$twitter", "$account_types" => ["merchant", "premium"], - + + # Structured fields (KYC / geo / bot detection) + "$nationality" => "US", + "$year_of_birth" => 1985, + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$full", + "$provider" => "prove" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + }, + # Send this information from a BROWSER client. "$browser" => { "$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", @@ -465,7 +522,7 @@ def update_account() "$content_language" => "en-GB" } } - + return @@client.track("$update_account", properties) end @@ -585,7 +642,24 @@ def create_account() "$social_sign_on_type" => "$twitter", "$account_types" => ["merchant", "premium"], - + + # Structured fields (KYC / geo / bot detection) + "$nationality" => "US", + "$year_of_birth" => 1985, + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$basic", + "$provider" => "lexisnexis" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + }, + # Suggested Custom Fields "twitter_handle" => "billyjones", "work_phone" => "1-347-555-5921", @@ -618,7 +692,15 @@ def verification() "$verified_event" => "$login", "$reason" => "$automated_rule", # Verification was triggered based on risk score "$verification_type" => "$sms", - "$verified_value" => "14155551212" + "$verified_value" => "14155551212", + + # Structured fields (KYC) + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$basic", + "$bin_nationality_match" => false, + "$provider" => "lexisnexis" + } } return @@client.track("$verification", properties) @@ -828,7 +910,22 @@ def update_order() "coupon_code" => "dollarMadness", "shipping_choice" => "FedEx Ground Courier", "is_first_time_buyer" => false, - + + # Structured fields (KYC / geo / bot detection) + "$kyc" => { + "$names_match" => true, + "$kyc_level" => "$basic", + "$provider" => "lexisnexis" + }, + "$geo" => { + "$uuid" => "gc-abc-123", + "$provider" => "geocomply" + }, + "$bot_identification" => { + "$result" => "$human", + "$provider" => "datadome" + }, + # Send this information from a BROWSER client. "$browser" => { "$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", @@ -836,7 +933,7 @@ def update_order() "$content_language" => "en-GB" } } - + return @@client.track("$update_order", properties) end From 5309e0da30b05bd33e265928de348e32e5c98e82 Mon Sep 17 00:00:00 2001 From: Raghu Kumar Date: Wed, 2 Sep 2026 13:02:45 -0700 Subject: [PATCH 3/4] NEB-4781: Make changelog entry more specific --- HISTORY | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/HISTORY b/HISTORY index 98d0b6e..139520d 100644 --- a/HISTORY +++ b/HISTORY @@ -1,5 +1,7 @@ === 4.7.0 2026-08-27 -- Added support for KYC, geo, and bot-detection structured fields ($nationality, $year_of_birth, $kyc, $geo, $bot_identification) on existing event types +- Added support for $nationality, $year_of_birth fields on $create_account, $update_account events +- Added support for $kyc field on $create_account, $update_account, $transaction, $create_order, $update_order, $verification events +- Added support for $geo, $bot_identification fields on $create_account, $update_account, $login, $transaction, $create_order, $update_order events === 4.6.0 2026-02-10 - Bump the minimum version of httparty to 0.23.3 to ensure protection against CVE-2025-68696 From cac445e0ae25ed4d5ce4fde836c0def0978ff14d Mon Sep 17 00:00:00 2001 From: Raghu Kumar Date: Fri, 4 Sep 2026 13:40:50 -0700 Subject: [PATCH 4/4] Remove unnecessary comments in test_events_api.rb per review --- test_integration_app/events_api/test_events_api.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test_integration_app/events_api/test_events_api.rb b/test_integration_app/events_api/test_events_api.rb index 68e6f79..6817bee 100644 --- a/test_integration_app/events_api/test_events_api.rb +++ b/test_integration_app/events_api/test_events_api.rb @@ -25,7 +25,6 @@ def login() "$site_domain" => "sift.com", "$site_country" => "US", - # Structured fields (geo / bot detection) "$geo" => { "$uuid" => "gc-abc-123", "$provider" => "geocomply" @@ -118,7 +117,6 @@ def transaction() # For marketplaces, use $seller_user_id to identify the seller "$seller_user_id" => "slinkys_emporium", - # Structured fields (KYC / geo / bot detection) "$kyc" => { "$names_match" => true, "$bin_nationality_match" => false, @@ -259,7 +257,6 @@ def create_order() "shipping_choice" => "FedEx Ground Courier", "is_first_time_buyer" => false, - # Structured fields (KYC / geo / bot detection) "$kyc" => { "$names_match" => true, "$kyc_level" => "$basic", @@ -498,7 +495,6 @@ def update_account() "$social_sign_on_type" => "$twitter", "$account_types" => ["merchant", "premium"], - # Structured fields (KYC / geo / bot detection) "$nationality" => "US", "$year_of_birth" => 1985, "$kyc" => { @@ -643,7 +639,6 @@ def create_account() "$social_sign_on_type" => "$twitter", "$account_types" => ["merchant", "premium"], - # Structured fields (KYC / geo / bot detection) "$nationality" => "US", "$year_of_birth" => 1985, "$kyc" => { @@ -694,7 +689,6 @@ def verification() "$verification_type" => "$sms", "$verified_value" => "14155551212", - # Structured fields (KYC) "$kyc" => { "$names_match" => true, "$kyc_level" => "$basic", @@ -911,7 +905,6 @@ def update_order() "shipping_choice" => "FedEx Ground Courier", "is_first_time_buyer" => false, - # Structured fields (KYC / geo / bot detection) "$kyc" => { "$names_match" => true, "$kyc_level" => "$basic",