reCAPTCHA Integration
This guide explains how reCAPTCHA validation (v2 and v3) works with the Swarm SDK.
Download page as PDFOverview
The Swarm SDK handles all human verification internally. Once the session is established, the SDK automatically intercepts protected requests, performs verification, and retries the original request on success.
reCAPTCHA v3
Silent background verification. The SDK calls
window.grecaptcha.execute() automatically.reCAPTCHA v2
Challenge widget verification. The SDK renders the widget into a
#g-recaptcha container you provide in the DOM.Setup - useInitSocket
Initialize the SDK at your application root. Required params are marked; all others are optional.
.ts
import {
useInitSocket
} from '@softconstruct/betchain-core-client/hooks';
useInitSocket({
/* -- required ----------------------------------------------------- */
siteId: 100,
language: 'en',
socketUrl: 'wss://your-socket-url',
onSessionSuccess: (data) => console.log('Session ready', data),
onSessionError: ({
message
}) => console.error('Session error', message),
/* -- optional ----------------------------------------------------- */
source: 1,
afec: userHash,
releaseDate: new Date(),
reCaptchaPlatform: 'android', // native apps only, see the native guide below
onReconnectSuccess: () => console.log('Reconnected'),
onReconnectError: ({
message
}) => console.error('Reconnect failed', message),
xPoint: {
clientKey: 'your-xpoint-client-key',
enableXPoint: true,
onResolve: ({
value
}) => {
// value is '' when the user is allowed, or the X_POINT sentinel when blocked.
},
},
});Session Initialization & reCAPTCHA Configuration
After connecting, the SDK receives verification configuration from the server via the session handshake. This data is also forwarded to your
onSessionSuccess callback..ts
onSessionSuccess: (data) => {
recaptcha_enabled: true,
recaptcha_version: 3,
recaptcha_actions: ['reset_password', 'register_user', ...],
site_key: '6LcRh3EqAAAAADryiHcUU4h5-iKmCcI5wPqCe7Iu',
};The SDK stores this data internally and uses it to decide whether verification is required before each protected request.
| Field | Example value | Description |
|---|---|---|
| recaptcha_enabled | true | Whether reCAPTCHA validation is active on the server. |
| recaptcha_version | 3 | Which reCAPTCHA version the server expects (2 or 3). |
| recaptcha_actions | ['reset_password', ...] | List of command names that require reCAPTCHA validation. |
| site_key | "6LcRh3EqAAAAADryiHcUU4h5-iKmCcI5wPqCe7Iu" | The Google reCAPTCHA site key used internally by the SDK to generate tokens. |
reCAPTCHA v3 Flow
Under the hood
The SDK initializes reCAPTCHA v3 by dynamically injecting the Google script into the page using the
site_key received from the session:.sh
https://www.recaptcha.net/recaptcha/api.js?render=${siteKey}Once the script is loaded, the SDK calls
window.grecaptcha.execute(siteKey, { action }); via an internal helper to obtain a token. If the script fails to load or window.grecaptcha is unavailable, the helper throws recaptcha_v3_initialization_failed.How it works
- A protected request is made, for example forgot password.
- The SDK checks
recaptcha_enabledand whether the command requires verification according torecaptcha_actions. - The SDK silently calls
window.grecaptcha.execute(siteKey, { action })to obtain a token. - The SDK sends a v3 validation request to the server with the token.
- Token valid: the original request is automatically retried.
- Token invalid: the listener receives an error payload.
Making a protected request
Consumer code does not need to provide a reCAPTCHA token manually. The SDK intercepts the request, performs v3 verification transparently, and then executes the original request if verification passes.
.ts
import {
forgotPassword
} from '@softconstruct/betchain-core-client/swarm';
import {
useSwarmGetOnce
} from '@softconstruct/betchain-core-client/hooks';
const {
makeRequest
} = useSwarmGetOnce(forgotPassword, {
shouldWait: true
});
await makeRequest({
email: 'new_email',
});reCAPTCHA v2 Flow
reCAPTCHA v2 can be triggered in two ways. In both cases the SDK renders the challenge widget and, after the user completes it, automatically retries the pending request. No form re-submission is needed.
Under the hood
The reCAPTCHA v2 script must be loaded on the page so that
window.grecaptcha is available.A container element with
id="g-recaptcha" must exist in the DOM. The SDK looks up this element by ID and renders the widget inside it. If the element is not found, the SDK returns g_recaptcha_element_not_found..html
<div id="g-recaptcha" data-testid="g-recaptcha" data-verifycallbackname="verifyCallback"></div>
1. Pre-request v2 check
- If the session
recaptcha_versionis 2 and the command is inrecaptcha_actions, the SDK triggers v2 verification before the request is sent. - The SDK calls
window.grecaptcha.render()into the#g-recaptchacontainer. - The user completes the widget challenge.
- The SDK automatically calls the v2 validation endpoint with the widget token.
- On success, the SDK retries the original request automatically.
2. Server-initiated v2 (code 27)
- The server may return response code 27 after a v3 validation attempt, indicating v2 verification is required instead.
- The SDK renders the v2 widget into the
#g-recaptchacontainer. - The listener receives
{ hasError: true, error: 'reCaptcha v2 validation required' }to signal the error state to your UI. - The user completes the widget challenge.
- The SDK automatically calls the v2 validation endpoint.
- On success, the SDK retries the pending request automatically.
Error States
| Scenario | Listener payload |
|---|---|
| v3 token invalid (server rejected) | { hasError: true, error: 'recaptcha_v3_verification_failed' } |
| v3 script not loaded / window.grecaptcha unavailable | { hasError: true, error: 'recaptcha_v3_initialization_failed' } |
| Server requires v2 (code 27) | { hasError: true, error: 'recaptcha_v2_validation required' } |
| site_key missing from session (reCAPTCHA) | { hasError: true, error: 'missing_recaptcha_v2_siteKey' } or 'missing_recaptcha_v3_siteKey' |
| #g-recaptcha element not found in the DOM (v2) | { hasError: true, error: 'g_recaptcha_element_not_found' } |
Native app captcha (reCAPTCHA Enterprise)
The reCAPTCHA widget described above is a browser component, and a native Android or iOS app has no page to render it into. Google's only supported path for native apps is the reCAPTCHA Enterprise mobile SDK, whose tokens are verified through the Enterprise assessments API. Swarm now supports that flow end to end.
Invisible
There is no checkbox and no image challenge on mobile. The Enterprise SDK runs silently and returns a risk score.
Your app drives it
Once
reCaptchaPlatform is set, the BetChain Core SDK steps out of the way. Your app obtains the token and submits it for validation.The flow, end to end
- Your app opens a session and declares which platform it is, using
reCaptchaPlatform. - Swarm answers with the reCAPTCHA Enterprise
site_keyfor that platform. The app never hardcodes a key. - Your app initializes the Enterprise SDK with that key, once.
- Immediately before each protected action, your app calls the Enterprise SDK's
execute(action)and receives a short-lived token. - Your app validates the token with Swarm, then sends the actual protected command.
Before you start: what to send us
We create the Enterprise keys for your app, so we need the identifiers below. In return you get confirmation that native captcha is enabled for your partner ID and platform.
| What we need | Platform | Where to find it |
|---|---|---|
| Package name | Android | The applicationId in your Gradle config, for example com.partner.app |
| SHA-256 signing fingerprint | Android | keytool -list -v -keystore <release-key>.jks — the SHA-256 line of your production release key |
| Bundle ID | iOS | The bundle identifier in Xcode, for example com.partner.app |
| Distributed outside Google Play? | Android | Yes or no. APK-direct and third-party stores need a separate key |
| Apple team ID, key ID and .p8 | iOS (optional) | Improves score accuracy. Can be added later |
You never receive a key to hardcode. The site key is delivered to your app at runtime, in the session response.
Step 1 — Declare your platform when opening the session
Pass
reCaptchaPlatform to useInitSocket with the value "android" or "ios". The SDK carries it into the session handshake as the captcha_platform parameter of request_session..ts
import { useInitSocket } from '@softconstruct/betchain-core-client/hooks';
useInitSocket({
siteId: 123,
language: 'en',
socketUrl: 'wss://your-socket-url',
// 'android' or 'ios'. Send the OS the app runs on, not the framework.
reCaptchaPlatform: 'android',
onSessionSuccess: (data) => console.log('Session ready', data),
onSessionError: ({ message }) => console.error('Session error', message),
});- Any value other than
androidoriosis rejected. The SDK fails fast, callingonSessionErrorwithincorrect_recaptcha_platformand never opening the session; the same value sent directly is rejected by Swarm with code 2. - Omitting the parameter keeps today's behaviour.
- Setting it also switches off the SDK's own reCAPTCHA, Turnstile and XPoint handling, so your app owns verification from here on.
- React Native and Flutter: send the operating system the app is actually running on, not the framework. Use
Platform.OSin React Native, andPlatform.isAndroid/Platform.isIOSin Flutter.
Step 2 — Read the captcha config from the response
The SDK hands the session config to your
onSessionSuccess callback in camelCase. The raw request_session response carries the same values in snake_case..ts
onSessionSuccess: (data) => {
sid: '...',
recaptchaEnabled: true,
recaptchaEnterprise: true,
recaptchaActions: ['login', 'register_user', 'do_bet', 'deposit'],
};| onSessionSuccess | Session response | Meaning |
|---|---|---|
| recaptchaEnterprise | recaptcha_enterprise | true means native captcha is on for you. false, or absent, means it is not enabled for this partner and platform — do not initialize the Enterprise SDK. |
| recaptchaActions | recaptcha_actions | The actions your partner actually protects. Only these need a token. |
| recaptchaEnabled | recaptcha_enabled | false means captcha is switched off entirely for this partner. |
| — | site_key | The key to initialize the reCAPTCHA Enterprise SDK with. Read it at runtime on every session; never hardcode or persist it. |
Step 3 — Initialize the Enterprise SDK once
| Platform | Dependency | Minimum |
|---|---|---|
| Android | com.google.android.recaptcha:recaptcha:18.9.x (Google Maven) | API level 24 |
| iOS | RecaptchaEnterprise via Swift Package Manager | iOS 15 |
| Flutter | recaptcha_enterprise_flutter | As per the platform above |
| React Native | No official Google SDK for React Native. Wrap the native Android and iOS SDKs above in a thin native module. | As per the platform above |
Fetching the client
.kt
// Once, at app start. Keep the client for the whole app lifetime. val client = Recaptcha .fetchClient(application, siteKeyFromSwarm) .getOrThrow()
Signatures vary between SDK versions. Check the exact ones for your version in Google's mobile reference pages: instrument-android-apps, instrument-ios-apps, create-key-mobile and create-assessment-mobile under
https://docs.cloud.google.com/recaptcha/docs/.- Fetch the client once and reuse it. Do not create a new client per action.
- Android requires the
android.permission.INTERNETpermission. - CocoaPods support for the iOS SDK ends after the 18.9.x line. Use Swift Package Manager for new integrations.
Step 4 — Get a token immediately before each action
.kt
val token = client
.execute(RecaptchaAction.custom("do_bet"))
.getOrThrow()Token rules
- A token expires after two minutes and is single use. Never cache or reuse one.
- The action string you pass to
executemust be exactly the action you then send to Swarm. A mismatch is treated as an attack and rejected. - Action names are case-insensitive and may contain only letters, digits,
/and_. - One token per protected action. Do not fetch a token at screen load and use it later.
Step 5 — Validate the token, then send the command
Pass the token and its action to the validateRecaptchaV3 method — its page carries the full parameters, the response shape and a Try it now console. On success, send the protected command immediately.
.json
{
command: 'validate_recaptcha',
params: {
action: 'do_bet',
g_recaptcha_response: '<token from the mobile SDK>'
}
}- Both the token and the
actionare required. - Do not send
m_hashtogether with the token. Exactly one of the two is allowed, otherwise the call fails with code 2. - No session id is needed. The session is managed internally.
- Some commands also accept the token inline, for example
register_userinsideuser_infoandloginasg_recaptcha_response. Validate-then-command works for every protected command, so that is the pattern we recommend everywhere.
Do and do not
Do
- Read
site_keyfrom the session response on every session. - Call
executeimmediately before the protected command. - Use the same action string when you execute and when you validate.
- Handle SDK failures gracefully — no network, Play Services missing. Offer a retry rather than silently skipping the action.
Do not
- Reuse tokens, cache them, or fetch them in advance.
- Hardcode or persist the site key.
- Use a WebView or the web reCAPTCHA widget in a native app.
- Ship a key registered against a debug signing fingerprint to production.
Existing apps using m_hash
m_hash continues to work unchanged, so no app breaks on release day. It is a static shared-secret bypass that carries no device signals, and it is planned for retirement per partner once the Enterprise SDK integration is live.