# Friendly Captcha Developer Hub > Documentation and tutorials for integrating Friendly Captcha — widget SDK, server-side validation, risk intelligence, and enterprise features. ## docs ### v2 Friendly Captcha is a service that protects websites from bots and abuse in a privacy-friendly and accessible way. To learn more about the company or service, you can visit the Friendly Captcha website. - [Introduction](/docs/v2.md): Friendly Captcha is a service that protects websites from bots and abuse in a privacy-friendly and accessible way. To learn more about the company or service, you can visit the Friendly Captcha website. - [Server-side Validation](/docs/v2/api.md): Authentication - [Authentication](/docs/v2/api/authentication.md): To make requests to the Friendly Captcha API you need to prove who you are. You do this by passing an API key in a header. - [/api/v2/captcha/siteverify](/docs/v2/api/siteverify.md): The siteverify endpoint is used to verify captcha responses. A captcha response is the value that is embedded in your form by the captcha widget when the challenge has been completed. - [Customizing the Widget Banner Link](/docs/v2/customizing-widget-banner-link.md): While the widget's banner link color cannot be customized through the dashboard's theme settings, you can easily modify it using CSS on your webpage. - [Enterprise](/docs/v2/enterprise.md): Friendly Captcha is a service that protects websites from bots and abuse in a privacy-friendly and accessible way. - [Compliance+ Add-On](/docs/v2/enterprise/compliance-plus-add-on.md): The Friendly Captcha Compliance+ Add-On is an optional extra on the Enterprise plan. It is tailor-made to help you reduce compliance effort and risk. - [Audit Logs](/docs/v2/enterprise/compliance-plus-add-on/audit-logs.md): The Audit Logs feature is part of the Compliance+ Add-On. - [Enhanced Access Control](/docs/v2/enterprise/compliance-plus-add-on/enhanced-access-control.md): The Enhanced Access Control feature is part of the Compliance+ Add-On. - [Single Sign-On (SSO)](/docs/v2/enterprise/compliance-plus-add-on/single-sign-on.md): The Single Sign-On feature is part of the Compliance+ Add-On. - [Getting Started](/docs/v2/getting-started.md): Friendly Captcha is a service that protects your websites and applications from automated abuse. - [Install the widget](/docs/v2/getting-started/install.md): Option A: HTML Scripts - [Create a sitekey](/docs/v2/getting-started/setup.md): Log in to your Friendly Captcha account and head to the Applications page. - [Verify the response](/docs/v2/getting-started/verify.md): When the captcha challenge is completed by the user, the user's browser receives a piece of text that proves that they completed the challenge. This proof is called the response. - [Guides](/docs/v2/guides.md): Advanced - [Automated Testing](/docs/v2/guides/automated-testing.md): Testing can be at odds with a captcha you add to your website or app. Perhaps you use automated testing tools like Cypress, Selenium, or Puppeteer. - [Browser Support](/docs/v2/guides/browser-support.md): All modern browsers are supported, on both mobile and desktop, all releases up to at least 8 years old. - [Content Security Policy (CSP)](/docs/v2/guides/csp.md): Content Security Policy is a way to secure your website from cross-site scripting (XSS). - [Dedicated EU Endpoint](/docs/v2/guides/eu-endpoint.md): By default the FriendlyCaptcha widget talks to our global service served from all over the world to retrieve CAPTCHA challenges. Depending on your user's geography, this request may be served from outside the EU. - [Invisible CAPTCHA](/docs/v2/guides/invisible-captcha.md): To create a fully invisible user experience, it is possible to hide the Friendly Captcha widget on your site. Before doing so, there are some considerations to make. This guide will explain the considerations and then show how to safely hide the widget. - [Language Detection](/docs/v2/guides/localization.md): If your website is in German it makes sense to have the widget in German too. - [Migrating from hCaptcha](/docs/v2/guides/migrating-from-hcaptcha.md): Switching from hCaptcha to Friendly Captcha is straightforward. - [Migrating from reCAPTCHA](/docs/v2/guides/migrating-from-recaptcha.md): Switching from Google's reCAPTCHA (v2) to Friendly Captcha is straightforward. - [Risk-Based Authentication using Risk Intelligence](/docs/v2/guides/risk-based-authentication-using-risk-intelligence.md): This guide shows how to build a simple risk-based authentication flow using Risk Intelligence. Risk-based authentication means using signals from the browsing session to make logging in more difficult (or easier) depending on the perceived risk level. In this particular example, we will use Risk Intelligence data to require an additional confirmation when the user is logging in from an unrecognized browser. - [Self-Hosted Endpoint](/docs/v2/guides/self-hosted-endpoint.md): The Self-Hosted Endpoint feature is only available for Friendly Captcha v2. - [Upgrading from v1 to v2](/docs/v2/guides/upgrading-from-v1.md): This guide describes how to upgrade from v1 to v2 of Friendly Captcha. - [Backend Integration](/docs/v2/guides/upgrading-from-v1/backend-integration.md): 1. Change the field name in your forms - [Client (JavaScript API)](/docs/v2/guides/upgrading-from-v1/javascript-api.md): 1. Install the new SDK - [Client (CDN script)](/docs/v2/guides/upgrading-from-v1/script.md): This guide runs you through the changes you will have to make when switching from version 1 to version 2 of the Friendly Captcha widget if you are using the script tags. - [Captcha Warning](/docs/v2/guides/widget-warning.md): Friendly Captcha may display a warning message that indicates billing problems or other misconfigurations. This page explains what each warning means and how to resolve it. - [Risk Intelligence](/docs/v2/risk-intelligence.md): Risk Intelligence helps make decisions about the trustworthiness of users interacting with your services. - [Format](/docs/v2/risk-intelligence/format.md): Risk Intelligence data is returned in a structured JSON format that includes various risk signals and scores. The exact structure of the data may vary depending on which modules you have enabled on your account (as of writing, all modules are included by default). - [Getting Started with Risk Intelligence](/docs/v2/risk-intelligence/getting-started.md): There are two parts to getting Risk Intelligence data. First, you generate a Risk Intelligence token**_ in your project's front-end. Second, you use that token to retrieve Risk Intelligence data_ from the Friendly Captcha API via your project's back-end. - [Generate a Risk Intelligence token](/docs/v2/risk-intelligence/getting-started/generate.md): You can generate a token directly using JavaScript, or you can use an HTML element to automatically generate a token and embed it in a `` element. - [Retrieve Risk Intelligence data](/docs/v2/risk-intelligence/getting-started/retrieve.md): To retrieve Risk Intelligence data using a token, you need to send a request containing the token authenticated by the Friendly Captcha API key you created in the first step. - [Create a sitekey and API key](/docs/v2/risk-intelligence/getting-started/setup.md): Sitekey - [Risk Intelligence On Captcha Challenge](/docs/v2/risk-intelligence/on-captcha-challenge.md): As a paid add-on we offer Risk Intelligence data when verifying any Friendly Captcha v2 challenge. This feature simplifies your integration by allowing you to get risk scores and signals about the user who solved the captcha challenge without needing to make a separate API call to our Risk Intelligence API or change your frontend integration. - [Use Cases](/docs/v2/risk-intelligence/use-cases.md): Risk Intelligence data can be used in various ways to enhance the security and fraud detection capabilities of your applications. Here we describe some common use cases for Risk Intelligence data. - [Widget SDK](/docs/v2/sdk.md): The SDK is the code you add to your website to display captcha widgets on your website. - [Configuration](/docs/v2/sdk/configuration.md): There are two ways you can configure the behavior of the widget. - [Events](/docs/v2/sdk/events.md): The captcha widget emits DOM events which allow you to write code that reacts to changes in the widget. - [Lifecycle](/docs/v2/sdk/lifecycle.md): The widget has a fixed set of states it can be in. - [API](/docs/v2/sdk/reference.md): Home - [sdk](/docs/v2/sdk/reference/sdk.md): @friendlycaptcha/sdk - [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md): @friendlycaptcha/sdk > APIEndpoint - [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md): @friendlycaptcha/sdk > CreateWidgetOptions - [CreateWidgetOptions.apiEndpoint](/docs/v2/sdk/reference/sdk.createwidgetoptions.apiendpoint.md): @friendlycaptcha/sdk > CreateWidgetOptions > apiEndpoint - [CreateWidgetOptions.element](/docs/v2/sdk/reference/sdk.createwidgetoptions.element.md): @friendlycaptcha/sdk > CreateWidgetOptions > element - [CreateWidgetOptions.formFieldName](/docs/v2/sdk/reference/sdk.createwidgetoptions.formfieldname.md): @friendlycaptcha/sdk > CreateWidgetOptions > formFieldName - [CreateWidgetOptions.language](/docs/v2/sdk/reference/sdk.createwidgetoptions.language.md): @friendlycaptcha/sdk > CreateWidgetOptions > language - [CreateWidgetOptions.sitekey](/docs/v2/sdk/reference/sdk.createwidgetoptions.sitekey.md): @friendlycaptcha/sdk > CreateWidgetOptions > sitekey - [CreateWidgetOptions.startMode](/docs/v2/sdk/reference/sdk.createwidgetoptions.startmode.md): @friendlycaptcha/sdk > CreateWidgetOptions > startMode - [CreateWidgetOptions.theme](/docs/v2/sdk/reference/sdk.createwidgetoptions.theme.md): @friendlycaptcha/sdk > CreateWidgetOptions > theme - [FRCEventData](/docs/v2/sdk/reference/sdk.frceventdata.md): @friendlycaptcha/sdk > FRCEventData - [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md): @friendlycaptcha/sdk > FRCEventMap - [FRCEventMap."frc:riskintelligence.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.complete_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:riskintelligence.complete" - [FRCEventMap."frc:riskintelligence.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.error_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:riskintelligence.error" - [FRCEventMap."frc:riskintelligence.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.expire_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:riskintelligence.expire" - [FRCEventMap."frc:widget.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.complete_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:widget.complete" - [FRCEventMap."frc:widget.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.error_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:widget.error" - [FRCEventMap."frc:widget.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.expire_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:widget.expire" - [FRCEventMap."frc:widget.reset"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.reset_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:widget.reset" - [FRCEventMap."frc:widget.statechange"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.statechange_.md): @friendlycaptcha/sdk > FRCEventMap > "frc:widget.statechange" - [FRCEventName](/docs/v2/sdk/reference/sdk.frceventname.md): @friendlycaptcha/sdk > FRCEventName - [FRCRiskIntelligenceCompleteEvent](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteevent.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEvent - [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEventData - [FRCRiskIntelligenceCompleteEventData.expiresAt](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.expiresat.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEventData > expiresAt - [FRCRiskIntelligenceCompleteEventData.name](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.name.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEventData > name - [FRCRiskIntelligenceCompleteEventData.token](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.token.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEventData > token - [FRCRiskIntelligenceCompleteEventName](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventname.md): @friendlycaptcha/sdk > FRCRiskIntelligenceCompleteEventName - [FRCRiskIntelligenceErrorEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceerrorevent.md): @friendlycaptcha/sdk > FRCRiskIntelligenceErrorEvent - [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md): @friendlycaptcha/sdk > FRCRiskIntelligenceErrorEventData - [FRCRiskIntelligenceErrorEventData.error](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.error.md): @friendlycaptcha/sdk > FRCRiskIntelligenceErrorEventData > error - [FRCRiskIntelligenceErrorEventData.name](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.name.md): @friendlycaptcha/sdk > FRCRiskIntelligenceErrorEventData > name - [FRCRiskIntelligenceErrorEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventname.md): @friendlycaptcha/sdk > FRCRiskIntelligenceErrorEventName - [FRCRiskIntelligenceExpireEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireevent.md): @friendlycaptcha/sdk > FRCRiskIntelligenceExpireEvent - [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md): @friendlycaptcha/sdk > FRCRiskIntelligenceExpireEventData - [FRCRiskIntelligenceExpireEventData.name](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.name.md): @friendlycaptcha/sdk > FRCRiskIntelligenceExpireEventData > name - [FRCRiskIntelligenceExpireEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventname.md): @friendlycaptcha/sdk > FRCRiskIntelligenceExpireEventName - [FRCWidgetCompleteEvent](/docs/v2/sdk/reference/sdk.frcwidgetcompleteevent.md): @friendlycaptcha/sdk > FRCWidgetCompleteEvent - [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventData - [FRCWidgetCompleteEventData.id](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.id.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventData > id - [FRCWidgetCompleteEventData.name](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.name.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventData > name - [FRCWidgetCompleteEventData.response](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.response.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventData > response - [FRCWidgetCompleteEventData.state](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.state.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventData > state - [FRCWidgetCompleteEventName](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventname.md): @friendlycaptcha/sdk > FRCWidgetCompleteEventName - [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData - [FRCWidgetErrorEventData.error](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.error.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData > error - [FRCWidgetErrorEventData.id](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.id.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData > id - [FRCWidgetErrorEventData.name](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.name.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData > name - [FRCWidgetErrorEventData.response](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.response.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData > response - [FRCWidgetErrorEventData.state](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.state.md): @friendlycaptcha/sdk > FRCWidgetErrorEventData > state - [FRCWidgetErrorEventName](/docs/v2/sdk/reference/sdk.frcwidgeterroreventname.md): @friendlycaptcha/sdk > FRCWidgetErrorEventName - [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md): @friendlycaptcha/sdk > FRCWidgetExpireEventData - [FRCWidgetExpireEventData.id](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.id.md): @friendlycaptcha/sdk > FRCWidgetExpireEventData > id - [FRCWidgetExpireEventData.name](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.name.md): @friendlycaptcha/sdk > FRCWidgetExpireEventData > name - [FRCWidgetExpireEventData.response](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.response.md): @friendlycaptcha/sdk > FRCWidgetExpireEventData > response - [FRCWidgetExpireEventData.state](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.state.md): @friendlycaptcha/sdk > FRCWidgetExpireEventData > state - [FRCWidgetExpireEventName](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventname.md): @friendlycaptcha/sdk > FRCWidgetExpireEventName - [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md): @friendlycaptcha/sdk > FRCWidgetResetEventData - [FRCWidgetResetEventData.id](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.id.md): @friendlycaptcha/sdk > FRCWidgetResetEventData > id - [FRCWidgetResetEventData.name](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.name.md): @friendlycaptcha/sdk > FRCWidgetResetEventData > name - [FRCWidgetResetEventData.response](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.response.md): @friendlycaptcha/sdk > FRCWidgetResetEventData > response - [FRCWidgetResetEventData.state](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.state.md): @friendlycaptcha/sdk > FRCWidgetResetEventData > state - [FRCWidgetResetEventData.trigger](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.trigger.md): @friendlycaptcha/sdk > FRCWidgetResetEventData > trigger - [FRCWidgetResetEventName](/docs/v2/sdk/reference/sdk.frcwidgetreseteventname.md): @friendlycaptcha/sdk > FRCWidgetResetEventName - [FRCWidgetStateChangeEvent](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeevent.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEvent - [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData - [FRCWidgetStateChangeEventData.error](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.error.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > error - [FRCWidgetStateChangeEventData.id](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.id.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > id - [FRCWidgetStateChangeEventData.mode](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.mode.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > mode - [FRCWidgetStateChangeEventData.name](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.name.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > name - [FRCWidgetStateChangeEventData.response](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.response.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > response - [FRCWidgetStateChangeEventData.state](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.state.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventData > state - [FRCWidgetStateChangeEventName](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventname.md): @friendlycaptcha/sdk > FRCWidgetStateChangeEventName - [FRCWidgetWidgetErrorEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgeterrorevent.md): @friendlycaptcha/sdk > FRCWidgetWidgetErrorEvent - [FRCWidgetWidgetExpireEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetexpireevent.md): @friendlycaptcha/sdk > FRCWidgetWidgetExpireEvent - [FRCWidgetWidgetResetEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetresetevent.md): @friendlycaptcha/sdk > FRCWidgetWidgetResetEvent - [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK - [FriendlyCaptchaSDK.(constructor)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk._constructor_.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > (constructor) - [FriendlyCaptchaSDK.attach()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attach.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > attach - [FriendlyCaptchaSDK.attached](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attached.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > attached - [FriendlyCaptchaSDK.clear()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clear.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > clear - [FriendlyCaptchaSDK.clearRiskIntelligence()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clearriskintelligence.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > clearRiskIntelligence - [FriendlyCaptchaSDK.createWidget()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.createwidget.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > createWidget - [FriendlyCaptchaSDK.getAllRiskIntelligenceHandles()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallriskintelligencehandles.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > getAllRiskIntelligenceHandles - [FriendlyCaptchaSDK.getAllWidgets()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallwidgets.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > getAllWidgets - [FriendlyCaptchaSDK.getWidgetById()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getwidgetbyid.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > getWidgetById - [FriendlyCaptchaSDK.riskIntelligence()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.riskintelligence.md): @friendlycaptcha/sdk > FriendlyCaptchaSDK > riskIntelligence - [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md): @friendlycaptcha/sdk > FriendlyCaptchaSDKOptions - [FriendlyCaptchaSDKOptions.apiEndpoint](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.apiendpoint.md): @friendlycaptcha/sdk > FriendlyCaptchaSDKOptions > apiEndpoint - [FriendlyCaptchaSDKOptions.disableEvalPatching](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.disableevalpatching.md): @friendlycaptcha/sdk > FriendlyCaptchaSDKOptions > disableEvalPatching - [FriendlyCaptchaSDKOptions.startAgent](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.startagent.md): @friendlycaptcha/sdk > FriendlyCaptchaSDKOptions > startAgent - [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md): @friendlycaptcha/sdk > RiskIntelligenceClearOptions - [RiskIntelligenceClearOptions.apiEndpoint](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.apiendpoint.md): @friendlycaptcha/sdk > RiskIntelligenceClearOptions > apiEndpoint - [RiskIntelligenceClearOptions.sitekey](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.sitekey.md): @friendlycaptcha/sdk > RiskIntelligenceClearOptions > sitekey - [RiskIntelligenceErrorCode](/docs/v2/sdk/reference/sdk.riskintelligenceerrorcode.md): @friendlycaptcha/sdk > RiskIntelligenceErrorCode - [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md): @friendlycaptcha/sdk > RiskIntelligenceErrorData - [RiskIntelligenceErrorData.code](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.code.md): @friendlycaptcha/sdk > RiskIntelligenceErrorData > code - [RiskIntelligenceErrorData.detail](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.detail.md): @friendlycaptcha/sdk > RiskIntelligenceErrorData > detail - [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md): @friendlycaptcha/sdk > RiskIntelligenceGenerateData - [RiskIntelligenceGenerateData.expiresAt](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.expiresat.md): @friendlycaptcha/sdk > RiskIntelligenceGenerateData > expiresAt - [RiskIntelligenceGenerateData.token](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.token.md): @friendlycaptcha/sdk > RiskIntelligenceGenerateData > token - [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md): @friendlycaptcha/sdk > RiskIntelligenceHandle - [RiskIntelligenceHandle.addEventListener()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.addeventlistener.md): @friendlycaptcha/sdk > RiskIntelligenceHandle > addEventListener - [RiskIntelligenceHandle.getData()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getdata.md): @friendlycaptcha/sdk > RiskIntelligenceHandle > getData - [RiskIntelligenceHandle.getElement()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getelement.md): @friendlycaptcha/sdk > RiskIntelligenceHandle > getElement - [RiskIntelligenceHandle.removeEventListener()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.removeeventlistener.md): @friendlycaptcha/sdk > RiskIntelligenceHandle > removeEventListener - [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md): @friendlycaptcha/sdk > RiskIntelligenceOptions - [RiskIntelligenceOptions.apiEndpoint](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.apiendpoint.md): @friendlycaptcha/sdk > RiskIntelligenceOptions > apiEndpoint - [RiskIntelligenceOptions.bypassCache](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.bypasscache.md): @friendlycaptcha/sdk > RiskIntelligenceOptions > bypassCache - [RiskIntelligenceOptions.sitekey](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.sitekey.md): @friendlycaptcha/sdk > RiskIntelligenceOptions > sitekey - [SentinelResponse](/docs/v2/sdk/reference/sdk.sentinelresponse.md): @friendlycaptcha/sdk > SentinelResponse - [StartMode](/docs/v2/sdk/reference/sdk.startmode.md): @friendlycaptcha/sdk > StartMode - [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md): @friendlycaptcha/sdk > WidgetErrorCode - [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md): @friendlycaptcha/sdk > WidgetErrorData - [WidgetErrorData.code](/docs/v2/sdk/reference/sdk.widgeterrordata.code.md): @friendlycaptcha/sdk > WidgetErrorData > code - [WidgetErrorData.detail](/docs/v2/sdk/reference/sdk.widgeterrordata.detail.md): @friendlycaptcha/sdk > WidgetErrorData > detail - [WidgetErrorData.title](/docs/v2/sdk/reference/sdk.widgeterrordata.title.md): @friendlycaptcha/sdk > WidgetErrorData > title - [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md): @friendlycaptcha/sdk > WidgetHandle - [WidgetHandle.addEventListener()](/docs/v2/sdk/reference/sdk.widgethandle.addeventlistener.md): @friendlycaptcha/sdk > WidgetHandle > addEventListener - [WidgetHandle.destroy()](/docs/v2/sdk/reference/sdk.widgethandle.destroy.md): @friendlycaptcha/sdk > WidgetHandle > destroy - [WidgetHandle.getElement()](/docs/v2/sdk/reference/sdk.widgethandle.getelement.md): @friendlycaptcha/sdk > WidgetHandle > getElement - [WidgetHandle.getResponse()](/docs/v2/sdk/reference/sdk.widgethandle.getresponse.md): @friendlycaptcha/sdk > WidgetHandle > getResponse - [WidgetHandle.getState()](/docs/v2/sdk/reference/sdk.widgethandle.getstate.md): @friendlycaptcha/sdk > WidgetHandle > getState - [WidgetHandle.id](/docs/v2/sdk/reference/sdk.widgethandle.id.md): @friendlycaptcha/sdk > WidgetHandle > id - [WidgetHandle.isDestroyed](/docs/v2/sdk/reference/sdk.widgethandle.isdestroyed.md): @friendlycaptcha/sdk > WidgetHandle > isDestroyed - [WidgetHandle.removeEventListener()](/docs/v2/sdk/reference/sdk.widgethandle.removeeventlistener.md): @friendlycaptcha/sdk > WidgetHandle > removeEventListener - [WidgetHandle.reset()](/docs/v2/sdk/reference/sdk.widgethandle.reset.md): @friendlycaptcha/sdk > WidgetHandle > reset - [WidgetHandle.sitekey](/docs/v2/sdk/reference/sdk.widgethandle.sitekey.md): @friendlycaptcha/sdk > WidgetHandle > sitekey - [WidgetHandle.start()](/docs/v2/sdk/reference/sdk.widgethandle.start.md): @friendlycaptcha/sdk > WidgetHandle > start - [WidgetHandle.startMode](/docs/v2/sdk/reference/sdk.widgethandle.startmode.md): @friendlycaptcha/sdk > WidgetHandle > startMode - [WidgetMode](/docs/v2/sdk/reference/sdk.widgetmode.md): @friendlycaptcha/sdk > WidgetMode - [WidgetResetOptions](/docs/v2/sdk/reference/sdk.widgetresetoptions.md): @friendlycaptcha/sdk > WidgetResetOptions - [WidgetResetTrigger](/docs/v2/sdk/reference/sdk.widgetresettrigger.md): @friendlycaptcha/sdk > WidgetResetTrigger - [WidgetState](/docs/v2/sdk/reference/sdk.widgetstate.md): @friendlycaptcha/sdk > WidgetState - [Use Cases](/docs/v2/use-cases.md): A collection of articles on various uses for Friendly Captcha. - [Account Takeover](/docs/v2/use-cases/account-takeover.md): Account takeover (ATO) is a type of identity theft where an attacker gains unauthorized access to a user's account. - [API Protection](/docs/v2/use-cases/api-protection.md): Your websites and apps likely rely on APIs to function. These APIs are vulnerable to abuse and attacks, which can lead to data breaches, service disruptions, and other security issues. - [Brute Force Attacks](/docs/v2/use-cases/brute-force-attacks.md): A brute force attack, also known as brute forcing, is an attempt to guess a web user’s credentials by trying every possible combination of characters. For websites and applications, the end goal of a brute force attack is account takeover (ATO), where the attacker gains unauthorized access to a different user's account. - [Credential Stuffing](/docs/v2/use-cases/credential-stuffing.md): Credential stuffing is a cyberattack technique where attackers use automated tools to try large numbers of username and password combinations, often obtained from previous data breaches, to gain unauthorized access to user accounts. The goal is to exploit the fact that many people reuse passwords across multiple sites, allowing attackers to compromise accounts on various platforms using the same credentials. - [Fake Accounts](/docs/v2/use-cases/fake-accounts.md): Fake accounts are a common problem on platforms, and they can be used for a variety of malicious purposes, such as spreading misinformation, harassing other users, engaging in fraudulent activities, or abusing your service otherwise. These accounts are often created using automated bots, which can quickly generate large numbers of fake accounts to overwhelm a platform. - [Payment Fraud](/docs/v2/use-cases/payment-fraud.md): Payment fraud is a significant concern for businesses and consumers alike. It involves unauthorized transactions, identity theft, and various schemes that can lead to financial losses. To combat payment fraud, organizations can implement several strategies: - [Spam Protection](/docs/v2/use-cases/spam-protection.md): Spam is a common problem for websites and apps that allow user-generated content or have a public-facing form. Spammers can use automated bots to flood your site with unwanted content, which can degrade the user experience, damage your reputation, and even lead to security issues. - [v1 and v2](/docs/v2/versions.md): There are two versions of the Friendly Captcha service, called v1 and v2. The second version contains technical and non-technical enhancements that come from state-of-the-art improvements and learnings from v1. --- # Full Documentation Content # Introduction Friendly Captcha is a service that protects websites from bots and abuse in a privacy-friendly and accessible way. To learn more about the company or service, you can visit the [Friendly Captcha website](https://friendlycaptcha.com). This site contains the technical documentation that will help you integrate the Friendly Captcha's anti-abuse service into your website. Integration entails adding a widget to the websites you want to protect. The Friendly Captcha widget looks like this: ![Screenshot of the Friendly Captcha widget](/img/widget-v2-ready.png) *Screenshot of the Friendly Captcha Widget* ## What's different about Friendly Captcha?[​](#whats-different-about-friendly-captcha "Direct link to What's different about Friendly Captcha?") Other captcha systems like [Google reCAPTCHA](https://en.wikipedia.org/wiki/ReCAPTCHA) or [hCaptcha](https://hcaptcha.com) invent tasks for website users that are meant to be easy for humans but impossible for bots. An example is clicking on images that contain cars. These tasks are annoying for many of us, but they can even become a **barrier** for some users. Those with difficulty seeing or hearing, or with advanced age, or who just lack technical savvy, might struggle to complete them. Another reason many organizations adopt Friendly Captcha is for its commitment to data protection and user privacy. Friendly Captcha does not rely on user tracking or cookies for its protection service, and offers a true EU-only offering. ## How does it work?[​](#how-does-it-work "Direct link to How does it work?") The Friendly Captcha widget serves a cryptographic puzzle which is solved by the user's device. The Friendly Captcha system analyzes various signals to compute a risk score, which is used to increase the difficulty of the puzzle. The main idea is that abusers will get high risk scores and difficult puzzles, while normal users will get low risk scores and easy puzzles. This happens in the background, so that by the time a user is ready to submit a form on your website, the puzzle is often already solved. ## Next Steps[​](#next-steps "Direct link to Next Steps") * Follow the [**Getting Started**](/docs/v2/getting-started/.md) tutorial to add Friendly Captcha to your website in 3 steps. * Visit the [**Friendly Captcha website**](https://friendlycaptcha.com). * Migrating from **Google reCAPTCHA** or **hCAPTCHA**? Follow our [**migration guides**](/docs/v2/guides/.md). --- # Server-side Validation ## [**Authentication**](/docs/v2/api/authentication.md)[​](#authentication "Direct link to authentication") [Authentication](/docs/v2/api/authentication.md) is how you prove to our servers that it's you making the request. You generally use API keys for that. ## Endpoints[​](#endpoints "Direct link to Endpoints") ### [`/api/v2/captcha/siteverify`](/docs/v2/api/siteverify.md)[​](#apiv2captchasiteverify "Direct link to apiv2captchasiteverify") The **Siteverify** endpoint is used to verify the captcha responses you receive from your users. --- # Authentication To make requests to the Friendly Captcha API you need to prove who you are. You do this by passing an API key in a header. caution API Keys are secret, please treat them the same way you would handle any other password in your organization. ## `X-API-Key` Header[​](#x-api-key-header "Direct link to x-api-key-header") With every request from your server, you should pass the following header: | Header Name | Header Value | | ----------- | ---------------------- | | `X-API-Key` | Your API Key (`A1...`) | ## Creating API Keys[​](#creating-api-keys "Direct link to Creating API Keys") You can create API keys in the [Friendly Captcha Dashboard](https://app.friendlycaptcha.eu/dashboard/accounts/-/apikeys). ### Some notes[​](#some-notes "Direct link to Some notes") * API keys can be used to verify captcha respones for all sitekeys in your account. * There is no cost to creating more API keys. * An API key can only be viewed when it is first created, make sure to copy it somewhere safe. If you lose your API key, you should create a new one. --- # `/api/v2/captcha/siteverify` The siteverify endpoint is used to verify captcha responses. A captcha response is the value that is embedded in your form by the captcha widget when the challenge has been completed. ## Request[​](#request "Direct link to Request") Make a **POST** request to `https://global.frcapi.com/api/v2/captcha/siteverify` with the following parameters: | POST Parameter | Type | Description | | -------------- | -------- | ----------------------------------------------------------------------------------- | | `response` | `string` | The response value that the user submitted in the `frc-captcha-response` field. | | `sitekey` | `string` | **Optional:** the sitekey that you want to make sure the puzzle was generated from. | note The maximum size of the `response` value is 16KB. tip Remember to authenticate the request [using the `X-API-Key` header](/docs/v2/api/authentication.md). You can pass these parameters in a JSON body, or as formdata. ## Response[​](#response "Direct link to Response") The response will tell you whether the captcha response is valid, meaning the captcha was solved succesfully. The response body is a JSON object which has a `success` field that tells you whether the response was valid or not. **Example `success=true` verification** ``` { "success": true, "data": { "event_id": "ev_CkK-YXwlFf-15_f", // Unique identifier for the siteverify request. "challenge": { "timestamp": "2026-02-05T13:01:25Z", // ISO 8601 timestamp when the captcha challenge was completed. "origin": "https://example.com" // Origin where the challenge happened. This can be empty if unknown. }, "risk_intelligence": null // `null` unless you have enabled Risk Intelligence on your account. See below. } } ``` **Example `success=false` response** ``` { "success": false, "error": { "error_code": "bad_request", // Error code, see the table below for possible values "detail": "..." // A human readable description of what went wrong. }, } ``` info We aim to ***never*** fundamentally change or remove *existing* fields from these responses. However, you should expect us to add *new* fields in the future. ### Error codes[​](#error-codes "Direct link to Error codes") Possible values for `error.error_code`. | Error code | Status | Description | | -------------------- | ------ | ------------------------------------------------------------------------------------------ | | `auth_required` | 401 | You forgot to set the `X-API-Key` header. | | `auth_invalid` | 401 | The API key you provided was invalid. | | `sitekey_invalid` | 400 | The sitekey in your request is invalid. | | `response_missing` | 400 | You forgot to add the response parameter. | | `response_invalid` | 200 | The response you provided was invalid (perhaps the user tried to work around the captcha). | | `response_timeout` | 200 | The response has expired. | | `response_duplicate` | 200 | The response has already been used. | | `bad_request` | 400 | Something else is wrong with your request, e.g. your request body is empty. | Warning Status code **200** does not mean the solution was valid. It means the verification was performed succesfully. Use the `success` field. **If you are seeing status code 400 or 401 your server code is probably not configured correctly.** tip **What to do when siteverify doesn't work** If you receive a response code other than 200 in production, you should probably accept the user's response despite not having been able to verify it. Maybe your server is misconfigured or the Friendly Captcha servers are down. While we try to make sure that never happens, it is a good idea to assume one day disaster will strike. It is better to temporarily accept bots or spam than to reject all requests. *Do remember to send an alert to yourself!* ## Risk Intelligence[​](#risk-intelligence "Direct link to Risk Intelligence") The `risk_intelligence` field in the successful response is `null` unless you have enabled [*Risk Intelligence on Captcha Challenge*](/docs/v2/risk-intelligence/on-captcha-challenge.md) on your account. Risk Intelligence allows you to receive risk scores and details about the solver of the request. This can help you make more informed decisions about how to handle the request, and allows you to feed the risk data into your fraud detection systems. This is a paid add-on feature that is available on **Friendly Captcha Advanced** and **Enterprise** plans. ## Troubleshooting & Tips[​](#troubleshooting--tips "Direct link to Troubleshooting & Tips") ### `method_not_allowed`[​](#method_not_allowed "Direct link to method_not_allowed") If you are seeing an error that includes `method_not_allowed`, you are likely making a HTTP **GET** request. You can fix this by making a HTTP **POST** request instead. ### How do I use automated testing with Friendly Captcha?[​](#how-do-i-use-automated-testing-with-friendly-captcha "Direct link to How do I use automated testing with Friendly Captcha?") You can mock out the siteverify response. Instead of talking to our API, your code could always return `{success: true}`. [This documentation page](/docs/v2/guides/automated-testing.md) has more details and alternative techniques. --- # Customizing the Widget Banner Link While the widget's banner link color cannot be customized through the dashboard's theme settings, you can easily modify it using CSS on your webpage. Here's the CSS you need to add to your page. Replace `` with the color you want to use. ``` .frc-banner a { color: !important; } @media (prefers-color-scheme: dark) { .frc-banner a { color: !important; } } ``` ## Best Practices[​](#best-practices "Direct link to Best Practices") To ensure end-user transparency and legal compliance, we recommend keeping the default banner link in the widget. You are allowed to hide the widget banner link as long as you include the Friendly Captcha attribution visibly in the user flow. Please include the following text: ``` This site is protected by Friendly Captcha and its Privacy Policy and Terms of Use apply. ``` --- # Enterprise Friendly Captcha is a service that protects websites from bots and abuse in a privacy-friendly and accessible way. The Friendly Captcha Enterprise plan offers specialized support for businesses and organizations with the most demanding requirements for security, scalability, and compliance. As your business grows, your protection requirements may exceed the limits in our *Starter*, *Growth*, and *Advanced* [pricing plans](https://friendlycaptcha.com/#pricing). Our **Enterprise** plan allows you to customize your service with Friendly Captcha to have maximum protection that scales with your business. The Enterprise plan includes these features: * Automatic risk detection and smart difficulty scaling * Dedicated data centers (e.g. EU only) * 99.9% guaranteed uptime * User management * Professional analytics features * Reporting capabilities * App-specific difficulty controls * Software engineer support * Assistance with your procurement and evaluation processes Friendly Captcha is compliant with GDPR, CCPA, and cookie law. Our dedicated data centers provide maximum data protection and ensure that your users' personal data is processed locally. Our CAPTCHA is fully accessible and compliant with WCAG. [Contact us now](https://friendlycaptcha.com/contact/sales/) and we'd love to talk! ## Compliance+ Add-On[​](#compliance-add-on "Direct link to Compliance+ Add-On") The Friendly Captcha [Compliance+ Add-On](/docs/v2/enterprise/compliance-plus-add-on/.md) is an optional extra on the Enterprise plan. It is tailor-made to help you **reduce compliance effort and risk**. --- # Compliance+ Add-On The Friendly Captcha Compliance+ Add-On is an optional extra on the [Enterprise plan](/docs/v2/enterprise/.md). It is tailor-made to help you **reduce compliance effort and risk**. The core features of the Compliance+ Add-On include: * [Single Sign-On (SSO)](/docs/v2/enterprise/compliance-plus-add-on/single-sign-on.md) * Bring your own identity provider to manage authentication to the Friendly Captcha dashboard. * [Enhanced Access Control](/docs/v2/enterprise/compliance-plus-add-on/enhanced-access-control.md) * Precise control over what each user can view or modify in the Friendly Captcha dashboard. * [Audit Logs](/docs/v2/enterprise/compliance-plus-add-on/audit-logs.md) * Persistent records of actions and changes made by users in your Friendly Captcha dashboard. --- # Audit Logs info The Audit Logs feature is part of the [**Compliance+ Add-On**](/docs/v2/enterprise/compliance-plus-add-on/.md). The Audit Logs feature provides comprehensive tracking of changes made by you and your team members within your organization's Friendly Captcha dashboard. This lets you keep track of what changes were made, when those changes were made, and by which user. The activity trail created by the Audit Logs feature is persistent and cannot be modified, so it can help you to satisfy enterprise compliance requirements and can help with security investigations. Audit Logs are particularly useful when many different team members have access to make changes on your organization's Friendly Captcha dashboard. Audit Logs include activity like creating, modifying or deleting *Applications*, creating or deleting *API keys*, inviting users, and so on. When you combine Audit Logs with [Enhanced Access Control](/docs/v2/enterprise/compliance-plus-add-on/enhanced-access-control.md), you will have tight control over security and have full oversight of changes that are made. This page shows you how you and your team members can use Audit Logs in the Friendly Captcha Dashboard. If you have any trouble, please do [contact support](https://friendlycaptcha.com/support/) and we'd be more than happy to help! ## Enabling Audit Logs[​](#enabling-audit-logs "Direct link to Enabling Audit Logs") Audit Logs are enabled by default for all customers with the [**Compliance+ Add-On**](/docs/v2/enterprise/compliance-plus-add-on/.md). ## Using Audit Logs[​](#using-audit-logs "Direct link to Using Audit Logs") ### Overview[​](#overview "Direct link to Overview") In the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Audit Logs** page. It looks like this: ![Screenshot of Audit Logs](/img/audit-logs.png) *Screenshot of Audit Logs* There are three columns: * *Description*: A summary of what changed. * *Actor*: The display name of the user that made this change. * *Timestamp*: The date and time of the change. ### Showing further details[​](#showing-further-details "Direct link to Showing further details") You can click on an item to show more detailed information. In the screenshot below, the first item in the list is expanded: ![Screenshot of the details for an Audit Logs entry](/img/audit-logs-detail.png) *Screenshot of the details for an Audit Logs entry* The details for every event will always include these two fields: * *Actor*: The user that made this change. * *Resource*: The resource that the change relates to (e.g. an *Application*, *API Key*, *Widget Theme*, etc). For some events, the details will also show the value before and after the change: * The old value *before* the change is shown by the line starting with a `-` symbol and is highlighted in red. * The new value *after* the change is shown by the line starting with a `+` symbol and is highlighted in green. ### Selecting a date range[​](#selecting-a-date-range "Direct link to Selecting a date range") If you want to view activity for a particular time period, click the date selector: ![Screenshot of Audit Logs date selector](/img/audit-logs-date.png) *Screenshot of Audit Logs date selector* ## Controlling who can view Audit Logs[​](#controlling-who-can-view-audit-logs "Direct link to Controlling who can view Audit Logs") Due to the sensitive nature of Audit Logs, only the user with the *Owner* role can view Audit Logs. --- # Enhanced Access Control info The Enhanced Access Control feature is part of the [**Compliance+ Add-On**](/docs/v2/enterprise/compliance-plus-add-on/.md). The Enhanced Access Control feature gives you fine-grained access control over what each of your team members can view or modify within your organization's Friendly Captcha dashboard. This allows you to implement the principle of least-privilege by granting team members only the minimum permissions they need to perform their duties. You can create roles tailored to your organization's structure, such as view-only auditors, billing administrators, or developers with access to only specific *Applications*. This can help you to satisfy security best practices and enterprise compliance requirements. For many organizations, tightly controlling access and keeping an audit log of changes are critical components of cybersecurity and compliance. Enhanced Access Control combines well with the [Audit Logs](/docs/v2/enterprise/compliance-plus-add-on/audit-logs.md) feature to help you satisfy these requirements. Additionally, if you are using our [Single Sign-On (SSO)](/docs/v2/enterprise/compliance-plus-add-on/single-sign-on.md) feature to leverage your organization's existing identity provider for authentication, you can use Enhanced Access Control to create a *Default Role* with minimal (or even zero) permissions so that any team member logging in for the first time using SSO won't have more access than you intended. ## Features[​](#features "Direct link to Features") ### App Groups[​](#app-groups "Direct link to App Groups") If you have many *Applications* configured in the Friendly Captcha Dashboard, you may want some team members to only have access to a specific set of applications. To make this easier, you can create an *App Group* with one or more *Applications*, and then create a *Custom Role* that has view or edit permissions for this *App Group*. ### Custom Roles[​](#custom-roles "Direct link to Custom Roles") There are three default roles: * *Member*: This role can manage *Applications*, *API Keys*, and *Widget Themes*. * *Admin*: This role is like *Member*, but can also send invites to new users. * *Owner*: This role has maximum access, including deleting *Members* or changing their role, configuring [Single Sign-On](/docs/v2/enterprise/compliance-plus-add-on/single-sign-on.md), and viewing [Audit Logs](/docs/v2/enterprise/compliance-plus-add-on/audit-logs.md). If you have many team members that have access to your organization's Friendly Captcha Dashboard, you may want to have more fine-grained access control. Custom Roles lets you create new roles that have exactly the permissions you want, such as read-only access, or edit access to only one specific *App Group*. ## Enabling Enhanced Access Control[​](#enabling-enhanced-access-control "Direct link to Enabling Enhanced Access Control") Enhanced Access Control is enabled by default for all customers with the [**Compliance+ Add-On**](/docs/v2/enterprise/compliance-plus-add-on/.md). ## Managing Enhanced Access Control[​](#managing-enhanced-access-control "Direct link to Managing Enhanced Access Control") In the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Settings** page and find the **App Groups** and **Custom Roles** sections. They look like this: ![Screenshot of App Groups and Custom Roles settings](/img/app-groups-and-custom-roles-settings.png) *Screenshot of App Groups and Custom Roles settings* ### App Groups[​](#app-groups-1 "Direct link to App Groups") info There is a built-in App Group called **Default**. If you haven't created any additional App Groups yet, all of your *Applications* belong to the **Default** App Group. To add a new App Group, type a suitable name into the input box and click the *Add App Group* button: ![Screenshot of adding a new App Group](/img/app-groups-add.png) *Screenshot of adding a new App Group* Your new App Group should now be visible in the list of App Groups: ![Screenshot of the new App Group](/img/app-groups-created.png) *Screenshot of the new App Group* To assign an Application to your new App Group, navigate to the *Applications* page to see your list of Applications. Click the *Manage* button for the Application you want to assign. You should see a page like this: ![Screenshot of the Manage App page](/img/app-groups-manage-app.png) *Screenshot of the Manage App page* Click the *App Group* drop-down menu and select your new App Group, then click the *Save changes* button. You can repeat this step for any other Applications that you want to put into this App Group. Go back to the main *Applications* page to see your list of Applications. You can now see the name of the App Group in the details of each Application: ![Screenshot of the Applications page](/img/app-groups-applications.png) *Screenshot of the Applications page* info You cannot delete an App Group that still has Applications assigned to it. To delete an App Group, you first need to assign all of the Applications that are in that App Group to a different App Group. ### Custom Roles[​](#custom-roles-1 "Direct link to Custom Roles") To add a new Custom Role, click the *Add custom role* button: ![Screenshot of Add Custom Role button](/img/custom-roles-add.png) *Screenshot of the Add Custom Role button* You will be taken to a page where you can configure your new Custom Role: ![Screenshot of the Add Custom Role form](/img/custom-roles-form.png) *Screenshot of the Add Custom Role form* The form has these fields: * **Role Name**: The name for your new Custom Role. * **Account Permissions**: These are broad permissions that you can grant to this Custom Role. You might for example give the `Manage Billing` permission to a Custom Role designed for your finance team, but give them no other permissions. * **All Apps Permissions**: The permission level you set here will be granted to this Custom Role across all Applications. These are the available choices: * *Manage*: View, create, update, or delete Applications. * *Edit*: View or update Applications. * *View*: View all Applications. * *None*: Cannot view any Applications. * **App Group Permissions**: Here you can grant permissions to one or more specific App Groups. The permission levels (i.e. *Manage*, *Edit*, *View*) behave the same as for *All Apps Permissions* described above. In the example below, we will create a Custom Role that can *Manage Widget Themes*, *Manage API Keys*, has the *View* permission level for all Applications, and the *Manage* permission level for Applications in one specific App Group: ![Screenshot of the completed Custom Role form](/img/custom-roles-form-completed.png) *Screenshot of the completed Custom Role form* tip **All Apps Permissions** takes precedence over **App Group Permissions**. It behaves like the default permission level across all *App Groups*. You can then use **App Group Permissions** to grant increased permissions to a specific *App Group*. You cannot use **App Group Permissions** to restrict permissions below the level of permissions granted by **All Apps Permissions**. If you try to do this, the dashboard will display an error message. Click the *Save Changes* button when you are done. You will be taken back to the main *Settings* page, where you can see your new Custom Role: ![Screenshot of the new Custom Role](/img/custom-roles-created.png) *Screenshot of the new Custom Role* You can now assign specific users to your new Custom Role. In the *Settings* page, scroll to the *Members* section, click the *Role* drop-down menu next to the desired user, and select your new Custom Role: ![Screenshot of assigning a Custom Role to a user](/img/custom-roles-assign.png) *Screenshot of assigning a Custom Role to a user* tip When inviting a new user, you have to select a role for them when sending the invite. The choice of roles includes any Custom Roles you have created. info You cannot delete a Custom Role that still has users assigned to it. To delete a Custom Role, you first need to assign all of the users with that role to a different role. --- # Single Sign-On (SSO) info The Single Sign-On feature is part of the [**Compliance+ Add-On**](/docs/v2/enterprise/compliance-plus-add-on/.md). The Single Sign-On (SSO) feature allows you to bring your own identity provider to manage authentication to the Friendly Captcha dashboard. This lets you leverage the security you already have in place with your existing identity provider, such as multi-factor authentication (MFA), user lifecycle management, brute force attack prevention, and account takeover protection. It can therefore help you to satisfy enterprise compliance requirements, and means logins from your organization to the Friendly Captcha dashboard align with your internal security policies. Friendly Captcha SSO works using OpenID Connect (OIDC). Almost all identity providers support OIDC, including Microsoft Entra ID, Google Workspace, Okta, Auth0, Ping Identity, OneLogin, Amazon Cognito, Keycloak, Duo Security etc. Even if your identity provider isn't listed here, it is extremely likely that you can use your identity provider with Friendly Captcha. This page shows you how to configure SSO authentication in the Friendly Captcha dashboard, and how your team members can use it to log in. If you have any trouble, please do [contact support](https://friendlycaptcha.com/support/) and we'd be more than happy to help! ## Configuring SSO[​](#configuring-sso "Direct link to Configuring SSO") ### Identity Provider[​](#identity-provider "Direct link to Identity Provider") Before configuring Friendly Captcha SSO, log in to your Identity Provider's dashboard and create an app integration for Friendly Captcha. The terminology may vary depending on the provider, but look for where you can create a *"New Application"*, *"New Integration"*, *"New OIDC App"*, or similar. You may need to consult your Identity Provider's OIDC documentation. When setting up a new app integration, your Identity Provider will usually ask for one or more *Redirect URIs* (sometimes called *"Sign-in redirect URIs"*, *"Log-in callback URIs"*, or similar). This is the URL you should provide: ``` https://app.friendlycaptcha.eu/api/v1/login/sso/callback ``` Save your new app integration and proceed with the next steps. ### Friendly Captcha[​](#friendly-captcha "Direct link to Friendly Captcha") In the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Settings** page and find the **Single Sign-On (SSO)** section. It looks like this: ![Screenshot of single sign-on settings](/img/single-sign-on.png) *Screenshot of SSO settings* Please fill in these fields with information from your identity provider: * **Client ID** * **Client Secret** * **Issuer URL** Often this information can be found in your identity provider's administrative dashboard. If you are unsure what values to use, please consult your identity provider's OpenID Connect (OIDC) documentation. Once you have entered the **Issuer URL**, a few more fields will automatically appear: ![Screenshot of full single sign-on settings](/img/single-sign-on-full.png) *Screenshot of full SSO settings* The dashboard will try to automatically fill these additional fields with information gathered from your identity provider; this should only take a couple of seconds. In some cases this automatic process may not be supported by your identity provider, in which case please use information from your identity provider to complete the remaining fields: * **Authorization URL** * **Token URL** * **JWKS URL** Once you've completed all of the fields, click the **Create SSO Configuration** button to enable SSO for your Friendly Captcha organization. The information you entered will be saved and visible (except the *Client Secret*). info Existing users in your Friendly Captcha organization are unaffected and can continue to login via email, unless you delete their user accounts. The *Owner* in your Friendly Captcha organization cannot be deleted and must always login via email. The **Members** section of the Settings page shows you the list of users for your Friendly Captcha organization. SSO users are always shown with a *"Logs in with SSO"* label (instead of an email address): ![Screenshot of single sign-on users in the members section](/img/single-sign-on-members.png) *Screenshot of SSO users in the Members section* ### Configuring the Default Role[​](#configuring-the-default-role "Direct link to Configuring the Default Role") When a user logs in with SSO to the Friendly Captcha dashboard for the first time, the dashboard automatically creates a user account for them and assigns this new user to the Default Role (e.g. *Admin*, *Member*, etc). To configure the Default Role, go to the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Settings** page and find the **Single Sign-On (SSO)** area. Use the **Default Role** drop-down menu to select your desired option. See [Enhanced Access Control](/docs/v2/enterprise/compliance-plus-add-on/enhanced-access-control.md) for information about creating more tailored roles to use here. ![Screenshot of single sign-on default role selection](/img/single-sign-on-default-role.png) *Screenshot of SSO default role selection* tip Once the user has logged in for the first time, they will appear in the Members section of the Settings page. If needed, you and other administrators can change user roles from here. It is usually a good idea to make the Default Role more limited (e.g. *Member*) so that you can grant more extensive permissions manually on a case-by-case basis (e.g. *Admin*). If desired, it is possible to create a [**Custom Role**](/docs/v2/enterprise/compliance-plus-add-on/enhanced-access-control.md) with fewer permissions than a *Member*, or even with zero permissions (i.e. unable to view any resources on the dashboard), and then set this Custom Role as the Default Role. ## Logging in with SSO[​](#logging-in-with-sso "Direct link to Logging in with SSO") Assuming that SSO is configured for your Friendly Captcha organization using the steps above, team members can now log in to the Friendly Captcha dashboard with SSO using a special login link. info If you aren't the administrator for your Friendly Captcha organization and don't already have a login to the Friendly Captcha dashboard, ask a team member with access to share this SSO Login Link with you. To retrieve this link, log in to the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Settings** page and find the **Single Sign-On (SSO)** area. You will see a *Copy Login Link* button: ![Screenshot of single sign-on button to copy the login link](/img/single-sign-on-copy-link.png) *Screenshot of SSO button to copy the login link* Click this button to copy the SSO Login Link to your clipboard and share it with your team members. We recommend storing the SSO Login Link somewhere easily accessible and discoverable, so that team members can find it when they want to access the Friendly Captcha dashboard. info When a user opens this link in their web browser, they will be redirected to your identity provider. For example, if your identity provider is Microsoft, the user will need to authenticate to their Microsoft account (unless they are already authenticated). Once authenticated, they will be redirected to the Friendly Captcha dashboard for your organization. ### Link caching[​](#link-caching "Direct link to Link caching") For convenience, this SSO Login Link will be cached in the user's browser after a successful login. If the user logs out and later navigates back to the Friendly Captcha Dashboard instead of using the SSO Login Link, they will be shown a *Log in with SSO* button. Clicking this button will behave the same as using the SSO Login Link. ![Screenshot of cached Log in with SSO button](/img/single-sign-on-cached-link.png) *Screenshot of cached "Log in with SSO" button* ## Disabling SSO[​](#disabling-sso "Direct link to Disabling SSO") To disable SSO for your Friendly Captcha organization, log in to the [Friendly Captcha Dashboard](https://app.friendlycaptcha.com/dashboard), navigate to the **Settings** page and find the **Single Sign-On (SSO)** area. You will see a *Delete SSO Configuration* button. Clicking this button will ask you to confirm deletion. danger If you disable SSO for your Friendly Captcha organization, all SSO users will also be permanently deleted. To see which users this will affect, review the Members section of the Settings page and look for users labelled with *"Logs in with SSO"*. --- # Getting Started Friendly Captcha is a service that protects your websites and applications from automated abuse. This guide shows you how to add our widget to your website to fight spam with little impact to your user's experience. **Integration has 3 steps and usually takes around 10 minutes to complete.** ## Create an account[​](#create-an-account "Direct link to Create an account") To add Friendly Captcha to your website you will need to [**sign up for an account at Friendly Captcha**](https://friendlycaptcha.com/signup). Try before you commit We offer a free 30-day trial, *no credit card required*. --- # Install the widget ## Option A: HTML Scripts[​](#option-a-html-scripts "Direct link to Option A: HTML Scripts") The easiest way to integrate Friendly Captcha into your website is by using the *`site`* script. This script automatically loads a widget for every element on your website with the `frc-captcha` class. info To create widgets from Javascript code, [see **Option B** below](#option-b-programmatically-javascript-sdk). ### Adding the scripts[​](#adding-the-scripts "Direct link to Adding the scripts") Add the following to your website's HTML: ``` ``` tip #### Using the scripts without a CDN (i.e. self-hosting)[​](#using-the-scripts-without-a-cdn-ie-self-hosting "Direct link to Using the scripts without a CDN (i.e. self-hosting)") Using `cdn.jsdelivr.net` is optional. If preferred, you can self-host the scripts. [Download the latest release files](https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0) and serve them from your own server. Remember to update these scripts regularly. `cdn.jsdelivr.net` is blocked in some jurisdictions, like some parts of China. If your website needs to be reachable from these jurisdictions, we recommend that you self-host the scripts. ### Adding the widget[​](#adding-the-widget "Direct link to Adding the widget") Insert a HTML element with the `frc-captcha` class in the form you want to protect. Use the sitekey you created in the previous step. ```
``` info You can customize the widget's behavior by using [HTML attributes](/docs/v2/sdk/configuration.md). ### Next steps[​](#next-steps "Direct link to Next steps") The next step is to [**verify the captcha response**](/docs/v2/getting-started/verify.md) in your backend server code. ## Option B: Programmatically (Javascript SDK)[​](#option-b-programmatically-javascript-sdk "Direct link to Option B: Programmatically (Javascript SDK)") ### Install the library[​](#install-the-library "Direct link to Install the library") You can install our NPM package to create widgets programmatically. ``` # using npm npm install @friendlycaptcha/sdk # using yarn yarn add @friendlycaptcha/sdk ``` ### Create widgets from your code[​](#create-widgets-from-your-code "Direct link to Create widgets from your code") ``` import { FriendlyCaptchaSDK } from "@friendlycaptcha/sdk" // Re-use this SDK if you are creating multiple widgets. const sdk = new FriendlyCaptchaSDK(); ``` ``` // HTML element that you want to mount the widget to. const mount = document.querySelector("#my-widget"); // Create the widget const widget = sdk.createWidget({ element: mount, sitekey: "" }); ``` info You can customize the widget's behavior and integrate with it using the SDK. See the [Widget SDK reference](/docs/v2/sdk/.md). ### Next steps[​](#next-steps-1 "Direct link to Next steps") The next step is to [**verify the captcha response**](/docs/v2/getting-started/verify.md) embedded in the request to your backend server. --- # Create a sitekey Log in to your Friendly Captcha account and head to the [Applications](https://app.friendlycaptcha.eu/dashboard/accounts/-/apps) page. Click **+ New Application** and enter the necessary details. After you've created the application, take note of the **sitekey** value under the application name—we'll need it in the next step. Did you know? A sitekey always starts with the characters `FC`. --- # Verify the response When the captcha challenge is completed by the user, the user's browser receives a piece of text that proves that they completed the challenge. This proof is called the **response**. The widget embeds this response in the form which is submitted to your server in a field called `frc-captcha-response`. Your server needs to talk to Friendly Captcha's API to make sure the response is valid. **Without this verification there is no protection**. info If you are using the Javascript SDK, you can also embed the response in any other action you want to protect. ## Verifying the solution (*siteverify*)[​](#verifying-the-solution-siteverify "Direct link to verifying-the-solution-siteverify") ### Create an API key[​](#create-an-api-key "Direct link to Create an API key") You will need an API key to prove it's you, you can create one on the [**API Keys page**](https://app.friendlycaptcha.eu/dashboard/accounts/-/keys) in the dashboard. ### Make the siteverify request[​](#make-the-siteverify-request "Direct link to Make the siteverify request") To verify the CAPTCHA solution, make a **POST** request to `https://global.frcapi.com/api/v2/captcha/siteverify` with the following parameters: | POST Parameter | Type | Description | | -------------- | -------- | ----------------------------------------------------------------------------------- | | `response` | `string` | The response value that the user submitted in the `frc-captcha-response` field. | | `sitekey` | `string` | **Optional:** the sitekey that you want to make sure the puzzle was generated from. | You can pass these parameters in a JSON body or as formdata. To authenticate the request, make the request with the following header: | Header | Description | | ----------- | --------------------------------------------- | | `X-API-Key` | The API key you created in the previous step. | ### The siteverify response[​](#the-siteverify-response "Direct link to The siteverify response") The response will tell you whether the captcha response is valid, meaning the captcha was solved succesfully. The response body is a JSON object which has a `success` field that tells you whether the response was valid or not. **Example `success=true` verification** ``` { "success": true, "data": { "event_id": "ev_CkK-YXwlFf-15_f", // Unique identifier for the siteverify request. "challenge": { "timestamp": "2026-02-05T13:01:25Z", // ISO 8601 timestamp when the captcha challenge was completed. "origin": "example.com" // Origin where the challenge happened. This can be empty if unknown. }, "risk_intelligence": null // `null` unless you have enabled Risk Intelligence on your account. } } ``` **Example `success=false` response** ``` { "success": false, "error": { "error_code": "bad_request", // Error code, see the table below for possible values "detail": "..." // Extra details (this is mainly intended for Friendly Captcha staff) }, } ``` info We aim to ***never*** fundamentally change or remove *existing* fields from these responses. However, you should expect us to add *new* fields in the future. Below is a list of possible values for `error_code`. **If you are seeing status code 400 or 401 your server code is probably not configured correctly.** | Error code | Status | Description | | -------------------- | ------ | ------------------------------------------------------------------------------------------ | | `auth_required` | 401 | You forgot to set the `X-API-Key` header. | | `auth_invalid` | 401 | The API key you provided was invalid. | | `sitekey_invalid` | 400 | The sitekey in your request is invalid. | | `response_missing` | 400 | You forgot to add the response parameter. | | `response_invalid` | 200 | The response you provided was invalid (perhaps the user tried to work around the captcha). | | `response_timeout` | 200 | The response has expired. | | `response_duplicate` | 200 | The response has already been used. | | `bad_request` | 400 | Something else is wrong with your request, e.g. your request body is empty. | Warning Status code **200** does not mean the solution was valid. It means the verification was performed succesfully. Use the `success` field. #### Why can a solution be invalid?[​](#why-can-a-solution-be-invalid "Direct link to Why can a solution be invalid?") A solution can be invalid for a number of reasons, perhaps the user submitted before the captcha widget was finished, or they are trying to use a response more than once. The first case can be prevented by disabling the submit button until the CAPTCHA has been completed succesfully. tip **What to do when siteverify doesn't work** If you receive a response code other than 200 in production, you should probably accept the user's response despite not having been able to verify it. Maybe your server is misconfigured or the Friendly Captcha servers are down. While we try to make sure that never happens, it is a good idea to assume one day disaster will strike. It is better to temporarily accept bots or spam than to reject all requests. *Do remember to send an alert to yourself!* ## API Reference[​](#api-reference "Direct link to API Reference") For more thorough docs, please see the [API Reference](/docs/v2/api/.md). --- # Guides ## Advanced[​](#advanced "Direct link to Advanced") * [Widget Language](/docs/v2/guides/localization.md) * [Browser Support](/docs/v2/guides/browser-support.md) * [Automated testing](/docs/v2/guides/automated-testing.md) * [Content Security Policy](/docs/v2/guides/csp.md) ## Migration Guides[​](#migration-guides "Direct link to Migration Guides") * [Switch from Google reCAPTCHA](/docs/v2/guides/migrating-from-recaptcha.md) to Friendly Captcha. * [Switch from hCaptcha](/docs/v2/guides/migrating-from-hcaptcha.md) to Friendly Captcha. ## Upgrading from v1 to v2[​](#upgrading-from-v1-to-v2 "Direct link to Upgrading from v1 to v2") * [Upgrade from Friendly Captcha v1 to v2](/docs/v2/guides/upgrading-from-v1/.md) --- # Automated Testing Testing can be at odds with a captcha you add to your website or app. Perhaps you use automated testing tools like [Cypress](https://www.cypress.io/), [Selenium](https://www.selenium.dev/), or [Puppeteer](https://github.com/puppeteer/puppeteer). This page has some tips and approaches that will help work out a testing story for pages that include a captcha widget. ## Testing tips[​](#testing-tips "Direct link to Testing tips") ### Mocking out the API[​](#mocking-out-the-api "Direct link to Mocking out the API") The easiest and recommended approach is to mock out the API in the backend. Instead of calling our [siteverify API](/docs/v2/api/siteverify.md) and getting the JSON response, you instead always use `{success: true}` when running in test mode. With this approach you can keep all other code in your application the same. ### IP or password-based gating[​](#ip-or-password-based-gating "Direct link to IP or password-based gating") If you perform automated end-to-end tests in production you may want to conditionally disable the captcha check for your test-runner. * If you know the IPs of the machines that run these tests you could skip [CAPTCHA verification](/docs/v2/getting-started/verify.md) by adding the IPs to an allowlist in your code or infrastructure. * If you do not know the IP beforehand, you could have your test-runner put a secret in the form submission that you check for. In other words, you can specify a password that your backend will accept for bypassing [CAPTCHA verification](/docs/v2/getting-started/verify.md). ### Dynamic button enabling[​](#dynamic-button-enabling "Direct link to Dynamic button enabling") Many websites that have a form protected using Friendly Captcha will only enable the submit button after the captcha widget is finished to prevent users from submitting without a valid captcha solution. When you are using a browser automation tool you may want to enable the button despite the captcha not being completed, we advice you achieve this by executing a snippet of Javascript (which is something you can do in all browser testing automation tools). Here's an example such snippet: ``` const button = document.querySelector("#my-button"); button.disabled = false; ``` --- # Browser Support All modern browsers are supported, on both mobile and desktop, all releases up to at least 8 years old. Supported browsers include Safari, Edge, Chrome, Firefox, and Opera. Internet Explorer 11 also works, with some sidenotes (see the [section below](#internet-explorer)). ## Polyfills[​](#polyfills "Direct link to Polyfills") note **The `compat.min.js` script includes polyfills, so you generally don't have to do add any polyfills manually.** If you want to manually include polyfills or are using our Javascript SDK, these are the relevant polyfills to add: * To support [Internet Explorer 11 and browsers from around 2014](https://caniuse.com/?search=promise), you will need a polyfill for [**Promise**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). * To support even older browsers (Chrome 9 through 37, Safari 5.1 through 7.1), you should also include a polyfill for [**Map**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), such as [this one](https://github.com/anonyco/Javascript-Fast-Light-Map-WeakMap-Set-And-WeakSet-JS-Polyfill). ## Internet Explorer[​](#internet-explorer "Direct link to Internet Explorer") Internet Explorer 11 is supported out of the box, but **the Javascript engine is very slow in Internet Explorer leading to a poor user experience**. The captcha challenge will likely take more than a minute to solve. Consider displaying a message to IE users that they should use a different browser. You can use this Javascript snippet to display a note after the widget in Internet Explorer only: ``` if (!!document.documentMode) { // Only true in Internet Explorer Array.prototype.slice.call(document.querySelectorAll(".frc-captcha")).forEach(function (element) { var messageElement = document.createElement("p"); messageElement.innerHTML = "The anti-robot check works better and faster in modern browsers such as Edge, Firefox, or Chrome. Please update your browser"; element.parentNode.insertBefore(messageElement, element.nextSibling); }); } ``` ## NoScript[​](#noscript "Direct link to NoScript") Users need to have Javascript enabled to solve the captcha. We recommend you add a note for users that have Javascript disabled: ``` ``` This will only be visible to users without Javascript enabled. --- # Content Security Policy (CSP) Content Security Policy is a way to secure your website from cross-site scripting (XSS). ## Configuring your CSP for Friendly Captcha[​](#configuring-your-csp-for-friendly-captcha "Direct link to Configuring your CSP for Friendly Captcha") If you are using a CSP for your website you will have to configure it to allow Friendly Captcha's iframes to be embedded. In most cases you will only need to add the `frame-src: https://*.frcapi.com` directive, for example: ``` # old header Content-Security-Policy: default-src 'self' # new header Content-Security-Policy: default-src 'self'; frame-src https://*.frcapi.com ``` --- # Dedicated EU Endpoint By default the FriendlyCaptcha widget talks to our global service served from all over the world to retrieve CAPTCHA challenges. Depending on your user's geography, this request may be served from outside the EU. As a premium feature we offer a dedicated endpoint hosted in Germany as an additional guarantee that the personal information (i.e. visitor IP addresses) never leave the EU. info Using this service requires a **Friendly Captcha Advanced** or **Enterprise** plan. ## Enabling the EU endpoint[​](#enabling-the-eu-endpoint "Direct link to Enabling the EU endpoint") Open your [Applications page](https://app.friendlycaptcha.eu/dashboard/accounts/-/apps) and click **Manage** on the app you want to enable the EU endpoint for. In the **API Endpoint** section you can enable and disable the endpoints that your widget can use to fetch CAPTCHA challenges. info We advise you to enable both for now. Later when you confirm everything is working you can disable the global endpoint. ## Configuring the widget[​](#configuring-the-widget "Direct link to Configuring the widget") The widget uses the global endpoint by default, so you will need to configure it to use the EU endpoint instead. You can use the `data-api-endpoint` HTML attribute: ```
``` Or if you are using the [JavaScript widget API](/docs/v2/sdk/configuration.md#javascript-sdk) you can specify it in the options passed in the constructor: ``` import { FriendlyCaptchaSDK } from "@friendlycaptcha/sdk" const sdk = new FriendlyCaptchaSDK(); const mount = document.querySelector("#my-widget"); const widget = sdk.createWidget({ element: mount, sitekey: "", apiEndpoint: "eu", }); ``` With this change, the widget will only ever make requests to our EU endpoint. No changes are required for the verification of submitted solutions. ## EU Verification Endpoint[​](#eu-verification-endpoint "Direct link to EU Verification Endpoint") Your servers can also use our EU endpoint for the verification of submitted challenge solutions. Instead of the [usual verification endpoint](/docs/v2/api/siteverify.md) your server makes the POST request to `https://eu.frcapi.com/api/v2/captcha/siteverify`. ## Reference[​](#reference "Direct link to Reference") For reference, here are the two `siteverify` endpoints for your back-end integration. ### 🌍 Global[​](#-global "Direct link to 🌍 Global") ``` https://global.frcapi.com/api/v2/captcha/siteverify ``` ### 🇪🇺 EU[​](#-eu "Direct link to 🇪🇺 EU") ``` https://eu.frcapi.com/api/v2/captcha/siteverify ``` When configuring the widget in your front-end, you can use the `eu` or `global` shorthand. ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") If your widget or the browser console shows **Endpoint not allowed** or **403 Forbidden**, double-check that the you enabled the configured endpoint for the given sitekey. --- # Invisible CAPTCHA To create a fully invisible user experience, it is possible to hide the Friendly Captcha widget on your site. Before doing so, there are some considerations to make. This guide will explain the considerations and then show how to safely hide the widget. Attribution If you choose to hide the widget, you must include the Friendly Captcha attribution visibly in the user flow. This helps to ensure end-user transparency and legal compliance. Please include the following text. ``` This site is protected by Friendly Captcha and its Privacy Policy and Terms of Use apply. ``` ## When should I show the widget?[​](#when-should-i-show-the-widget "Direct link to When should I show the widget?") ### The widget requires a click to complete[​](#the-widget-requires-a-click-to-complete "Direct link to The widget requires a click to complete") Under certain circumstances, the Friendly Captcha widget requires a click to complete. If that is the case, and the widget is hidden, your website users will be unable click it and the widget will never complete. They will therefore be unable to perform the protected website action. Whether or not the widget requires a click is determined by its [Widget Mode](https://developer.friendlycaptcha.com/docs/v2/sdk/configuration#widget-mode). If your widget is in **One-click** mode, you should not hide it. You can set the widget to **Zero-click** mode, but know that doing so slightly sacrifices protection. If you leave the widget in its default of **Smart** mode, it may or may not require a click—and you can therefore *conditionally* show the widget if a click is required. We show how to do that below. warning If your application’s Widget Mode is set to **One-click**, do not hide the widget. ### The widget does not complete within 5 seconds[​](#the-widget-does-not-complete-within-5-seconds "Direct link to The widget does not complete within 5 seconds") We make every effort to ensure that your customers are never waiting for the widget to complete. Rarely, it may happen that a customer receives a challenge that takes the widget longer. In those cases, it could make for a poor user experience if they are waiting on the widget to complete a challenge but they can’t see what’s going on. Therefore, we recommend displaying the widget if it hasn’t completed after 5 seconds. ### The widget encounters an error or expires[​](#the-widget-encounters-an-error-or-expires "Direct link to The widget encounters an error or expires") It is unlikely, but a widget might encounter an error before it completes. In that scenario, you should display the widget so that your end user can see what happened and decide what to do next. Another rare scenario in which you should show the widget is when it expires. This can happen if the widget completes, but then the user does not actually perform the protected action (e.g., submitting the form). After a long period of time, the widget’s solution will expire, and the widget will show a message indicating as such. ## How to (safely) hide the widget[​](#how-to-safely-hide-the-widget "Direct link to How to (safely) hide the widget") You can use CSS to hide the widget and a few lines of JavaScript to ensure that the widget is displayed in the above scenarios. ### HTML[​](#html "Direct link to HTML") ```
``` ### CSS[​](#css "Direct link to CSS") ``` .frc-captcha-hidden { display: none; } ``` ### JavaScript[​](#javascript "Direct link to JavaScript") ``` document.addEventListener("DOMContentLoaded", function () { const SHOW_AFTER_LONG_WAIT_MS = 5_000; const el = document.getElementById("my-widget-id"); function showCaptchaWidget() { el.classList.remove("frc-captcha-hidden"); } let timeout; el.addEventListener("frc:widget.statechange", function (e) { // Show the widget if the application's Widget Mode is "Smart" // and the widget determines that a click is required to complete. if (e.mode === "interactive") { showCaptchaWidget(); } // Show the widget if it takes more than 10 seconds to complete. // You may also consider showing a loading indicator. if (e.state === "requesting") { clearTimeout(timeout); timeout = setTimeout(function () { showCaptchaWidget(); }, SHOW_AFTER_LONG_WAIT_MS); } else if (e.state === "completed") { clearTimeout(timeout); } // Show the widget if it encounters an error or expires. if (e.state === "error" || e.state === "expired") { showCaptchaWidget(); } }); }); ``` --- # Language Detection If your website is in German it makes sense to have the widget in German too. tip In most cases you don't have to do anything. The widget will match the language of your website. ## Automatic language detection[​](#automatic-language-detection "Direct link to Automatic language detection") The widget looks at the first HTML `lang` attribute it finds above itself to determinte the language to use. A best practice for any HTML page is to have a `lang` set at the root level: ``` ``` ### Fallback[​](#fallback "Direct link to Fallback") If no `lang` attribute is found above the widget or an unsupported language is requested, the language is determined by the headers automatically sent by the user's browser. This is usually the user's operating system language. If that also results only in unsupported languages, the widget will default to English. ### Forcing a specific language[​](#forcing-a-specific-language "Direct link to Forcing a specific language") To force a specific language you can set the `lang` attribute on the HTML element the widget is mounted to. ```
``` If you are creating widgets programmatically you can use the `language` field in the [configuration object](/docs/v2/sdk/reference/sdk.createwidgetoptions.md). ## Language Codes[​](#language-codes "Direct link to Language Codes") | Language | Code | % Complete | | ----------------------------------------- | ------------------------------ | ---------- | | 🇪🇬 **Arabic** \| *العربية* | `"ar"`, `"ar-EG"` | 89% | | 🇪🇸 **Basque** \| *Euskera* | `"eu"`, `"eu-ES"` | 98% | | 🇧🇦 **Bosnian** \| *Bosanski* | `"bs"`, `"bs-BA"` | 95% | | 🇧🇬 **Bulgarian** \| *български* | `"bg"`, `"bg-BG"` | 91% | | 🇪🇸 **Catalan** \| *Català* | `"ca"`, `"ca-ES"` | 95% | | 🇨🇳 **Chinese (simplified)** \| *汉语* | `"zh"`, `"zh-CN"`, `"zh-Hans"` | 89% | | 🇹🇼 **Chinese (traditional)** \| *漢語* | `"zh-TW"`, `"zh-Hant"` | 89% | | 🇭🇷 **Croatian** \| *Hrvatski* | `"hr"`, `"hr-HR"` | 95% | | 🇨🇿 **Czech** \| *Čeština* | `"cs"`, `"cs-CZ"` | 88% | | 🇩🇰 **Danish** \| *Dansk* | `"da"`, `"da-DK"` | 91% | | 🇳🇱 **Dutch** \| *Nederlands* | `"nl"`, `"nl-NL"` | 100% | | 🇺🇸 **English** | `"en"`, `"en-US"` | 100% | | 🇬🇧 **English (UK)** | `"en-GB"` | 100% | | 🇪🇪 **Estonian** \| *Eesti keel* | `"et"`, `"et-EE"` | 96% | | 🇫🇮 **Finnish** \| *Suomi* | `"fi"`, `"fi-FI"` | 89% | | 🇫🇷 **French** \| *Français* | `"fr"`, `"fr-FR"` | 91% | | 🇩🇪 **German** \| *Deutsch* | `"de"`, `"de-DE"` | 96% | | 🇬🇷 **Greek** \| *Ελληνικά* | `"el"`, `"el-GR"` | 95% | | 🇮🇳 **Hindi** \| *हिंदी* | `"hi"`, `"hi-IN"` | 89% | | 🇭🇺 **Hungarian** \| *Magyar* | `"hu"`, `"hu-HU"` | 59% | | 🇮🇩 **Indonesian** \| *Indonesia* | `"id"`, `"id-ID"` | 89% | | 🇮🇹 **Italian** \| *Italiano* | `"it"`, `"it-IT"` | 91% | | 🇯🇵 **Japanese** \| *日本語* | `"ja"`, `"ja-JP"` | 91% | | 🇰🇷 **Korean** \| *오류* | `"ko"`, `"ko-KR"` | 95% | | 🇱🇻 **Latvian** \| *Latviešu valoda* | `"lv"`, `"lv-LV"` | 95% | | 🇱🇹 **Lithuanian** \| *Lietuvių kalba* | `"lt"`, `"lt-LT"` | 95% | | 🇳🇴 **Norwegian Bokmål** \| *Norsk Bokmål* | `"nb"`, `"nb-NO"` | 91% | | 🇵🇱 **Polish** \| *Polski* | `"pl"`, `"pl-PL"` | 91% | | 🇵🇹 **Portuguese** \| *Português* | `"pt"`, `"pt-PT"`, `"pt-BR"` | 91% | | 🇷🇴 **Romanian** \| *Romana* | `"ro"`, `"ro-RO"` | 95% | | 🇷🇺 **Russian** \| *Русский* | `"ru"`, `"ru-RU"` | 89% | | 🇷🇸 **Serbian** \| *Srpski* | `"sr"`, `"sr-RS"` | 95% | | 🇸🇰 **Slovak** \| *Slovenčina* | `"sk"`, `"sk-SK"` | 89% | | 🇸🇮 **Slovenian** \| *Slovenščina* | `"sl"`, `"sl-SI"` | 95% | | 🇪🇸 **Spanish** \| *Español* | `"es"`, `"es-ES"` | 91% | | 🇸🇪 **Swedish** \| *Svenska* | `"sv"`, `"sv-SE"` | 89% | | 🇹🇭 **Thai** \| *ไทย* | `"th"`, `"th-TH"` | 89% | | 🇹🇷 **Turkish** \| *Türkçe* | `"tr"`, `"tr-TR"` | 88% | | 🇺🇦 **Ukrainian** \| *Українська* | `"uk"`, `"uk-UA"` | 95% | | 🇻🇳 **Vietnamese** \| *Tiếng Việt* | `"vi"`, `"vi-VN"` | 89% | > This table was last updated 2026-06-23. Language not supported? Is your preferred language not in the table above? Please help by [**contributing translations**](https://poeditor.com/join/project/lrdZQ5Uk6D). --- # Migrating from hCaptcha Switching from [hCaptcha](https://www.hcaptcha.com/) to Friendly Captcha is straightforward. To make the switch even easier we offer a `hcaptcha` compatibility mode. Using that compatibility mode switching should take only a few minutes. ## Create a Friendly Captcha sitekey and API Key[​](#create-a-friendly-captcha-sitekey-and-api-key "Direct link to Create a Friendly Captcha sitekey and API Key") info To add the Friendly Captcha widget to your website you will need an account. You can [sign up here](https://app.friendlycaptcha.eu/dashboard/signup). Log in to the [Friendly Captcha Dashboard](https://app.friendlycaptcha.eu/dashboard/), then 1. [Create a new application](https://app.friendlycaptcha.eu/dashboard/accounts/-/apps) and take note of its sitekey. 2. [Create a new API key](https://app.friendlycaptcha.eu/dashboard/accounts/-/apikeys) and copy it somewhere safe. tip If you are using a CMS like WordPress, you probably don't have to do the below steps. Instead you would replace the hCaptcha plugin and install a plugin that supports Friendly Captcha. ## Update your website's integration[​](#update-your-websites-integration "Direct link to Update your website's integration") 1. Replace the hCaptcha script ``` ``` with the hCAPTCHA-compatible Friendly Captcha scripts. ``` ``` info If you are making use of [query parameters to configure hCaptcha](https://docs.hcaptcha.com/configuration), add them to both scripts. For example if the script URL ends with `?hl=fr`, add that to both the new script URLs. 2. Replace `h-captcha` with `frc-captcha` and update the sitekey ```
``` becomes ```
``` ## Update your server's integration[​](#update-your-servers-integration "Direct link to Update your server's integration") Follow the guide [here](/docs/v2/getting-started/verify.md) to set up your server's integration. ## Compatibility notes (only relevant for advanced usage)[​](#compatibility-notes-only-relevant-for-advanced-usage "Direct link to Compatibility notes (only relevant for advanced usage)") Making use of the compatibility scripts (`hcaptcha-site.min.js` and `hcaptcha-site.compat.min.js`) means that the Friendly Captcha widget will work without having to change any Javascript code on your website or web app. Some fields and methods do behave differently, here is a list of the different behavior: ### Container Configuration and render attributes ([docs](https://docs.hcaptcha.com/configuration#hcaptcha-container-configuration))[​](#container-configuration-and-render-attributes-docs "Direct link to container-configuration-and-render-attributes-docs") * `data-size` has no effect. The Friendly Captcha widget is fully responsive, you can make it any size you want. You can use plain CSS to resize it. * `data-chalexpired-callback` is never called, instead `data-expired-callback` is called when the captcha response expires. * `data-open-callback` is called when the widget starts solving. * `data-close-callback` has no effect and is never called. In hCaptcha this is called when the user closes the "click-the-pictures" challenge, which is not relevant. ### Javascript API ([docs](https://docs.hcaptcha.com/configuration#hcaptcha-container-configuration))[​](#javascript-api-docs "Direct link to javascript-api-docs") * `hcaptcha.getRespKey(widgetID)`([docs](https://docs.hcaptcha.com/configuration#hcaptchagetrespkeywidgetid)) always returns an empty string. --- # Migrating from reCAPTCHA Switching from [Google's reCAPTCHA (v2)](https://www.google.com/recaptcha/about/) to Friendly Captcha is straightforward. To make the switch even easier we offer a `recaptcha` compatibility mode. Using that compatibility mode switching should take only a few minutes. ## Create a Friendly Captcha sitekey and API Key[​](#create-a-friendly-captcha-sitekey-and-api-key "Direct link to Create a Friendly Captcha sitekey and API Key") info To add the Friendly Captcha widget to your website you will need an account. You can [sign up here](https://app.friendlycaptcha.eu/dashboard/signup). Log in to the [Friendly Captcha Dashboard](https://app.friendlycaptcha.eu/dashboard/), then 1. [Create a new application](https://app.friendlycaptcha.eu/dashboard/accounts/-/apps) and take note of its sitekey. 2. [Create a new API key](https://app.friendlycaptcha.eu/dashboard/accounts/-/apikeys) and copy it somewhere safe. tip If you are using a CMS like WordPress, you probably don't have to do the below steps. Instead you would replace the reCAPTCHA plugin and install a plugin that supports Friendly Captcha. ## Update your website's integration[​](#update-your-websites-integration "Direct link to Update your website's integration") 1. Replace the reCAPTCHA script ``` ``` with the reCAPTCHA-compatible Friendly Captcha scripts. ``` ``` 2. Replace `g-recaptcha` with `frc-captcha` and update the sitekey ```
``` becomes ```
``` ## Update your server's integration[​](#update-your-servers-integration "Direct link to Update your server's integration") Follow the guide [here](/docs/v2/getting-started/verify.md) to set up your server's integration. ## Compatibility notes (only relevant for advanced usage)[​](#compatibility-notes-only-relevant-for-advanced-usage "Direct link to Compatibility notes (only relevant for advanced usage)") Making use of the compatibility scripts (`recaptcha-site.min.js` and `recaptcha-site.compat.min.js`) means that the Friendly Captcha widget will work without having to change any Javascript code on your website or web app. Some fields and methods do behave differently, here is a list of the different behaviors: ### Container Configuration and render attributes ([docs](https://developers.google.com/recaptcha/docs/display#render_param))[​](#container-configuration-and-render-attributes-docs "Direct link to container-configuration-and-render-attributes-docs") * `data-size` has no effect. The Friendly Captcha widget is fully responsive, you can make it any size you want. You can use plain CSS to resize it. * `data-close-callback` has no effect and is never called. In hCaptcha this is called when the user closes the "click-the-pictures" challenge, which is not relevant. --- # Risk-Based Authentication using Risk Intelligence This guide shows how to build a simple **risk-based authentication** flow using [**Risk Intelligence**](/docs/v2/risk-intelligence/.md). Risk-based authentication means using signals from the browsing session to make logging in more difficult (or easier) depending on the perceived risk level. In this particular example, we will use Risk Intelligence data to require an additional confirmation when the user is logging in from an unrecognized browser. We’ll be starting from a basic [Express](https://expressjs.com/) app running on Node.js. This app renders a login form (which accepts any credentials) and then displays a minimal welcome page showing the number of times a given username has logged in. ![Login form and welcome screen for risk-based auth tutorial](/assets/images/risk-based-auth-ui-init-101aa6c7793e8e3cc410c5223e091826.jpg) If you want to follow along, you can check out the code from the [repository](https://github.com/FriendlyCaptcha/risk-intelligence-tutorial-node-express). The [starting code](https://github.com/FriendlyCaptcha/risk-intelligence-tutorial-node-express/tree/init) is available on the branch named `init`. Not production-ready This is a sample app built for showing how to use Risk Intelligence. It skips best practices in the interest of simplicity, so it should not be used as a starting point for a production project. To use Risk Intelligence, we need a Friendly Captcha sitekey and an API key. Instructions for creating those are available in the [**Getting Started**](/docs/v2/risk-intelligence/getting-started/setup.md) guide. ## Generating the Risk Intelligence token[​](#generating-the-risk-intelligence-token "Direct link to Generating the Risk Intelligence token") With our sitekey in hand, we are ready to [generate a Risk Intelligence token](/docs/v2/risk-intelligence/getting-started/generate.md). All we need to do is add 3 lines of code to the login form. This project uses the Pug templating engine, but of course this works with plain HTML too. ``` extends layout block content h1 Log In form(method="post" action="/login") label(for="username") Username input(type="text" id="username" name="username" required) label(for="password") Password input(type="password" id="password" name="password" required) + div.frc-risk-intelligence(data-sitekey=process.env.FRC_SITEKEY) button(type="submit") Log In + script(type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.min.js" async defer) + script(nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.compat.min.js" async defer) ``` **Self-hosting the front-end SDK scripts** Using `cdn.jsdelivr.net` is optional. If preferred, you can self-host the scripts. [Download the latest release files](https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0) and serve them from your own server. Remember to update these scripts regularly. `cdn.jsdelivr.net` is blocked in some jurisdictions, like some parts of China. If your website needs to be reachable from these jurisdictions, we recommend that you self-host the scripts. If you restart the server and refresh the page, then focus either of the text fields, a Risk Intelligence token will be generated. If you inspect the `
` in your browser’s developer tools, you’ll be able to find a hidden `` element with the token as its `value`. ![Developer tools inspector showing the hidden input element with Risk Intelligence token](/assets/images/dev-tools-risk-intelligence-token-60b736dd9ac8f2181f300066a0201e49.png) ### Prevent submitting the form without the token[​](#prevent-submitting-the-form-without-the-token "Direct link to Prevent submitting the form without the token") With our current implementation, a web user with a high network latency could theoretically enter their email and submit the form before the token has been generated. With a few changes, we can make the implementation a little more robust. 1. Start with the submit button `disabled`. 2. Generate the risk intelligence token immediately, rather than when the form is focused. 3. When token generation finishes, enable the button. All we need to do is add a `disabled` attribute to the button, a `data-start="auto"` attribute to the `
`, and a few lines of JavaScript. Here’s the final version of the login page markup: ``` extends layout block content h1 Log In form(method="post" action="/login") label(for="username") Username input(type="text" id="username" name="username" required) label(for="password") Password input(type="password" id="password" name="password" required) - div.frc-risk-intelligence(data-sitekey=process.env.FRC_SITEKEY) + div.frc-risk-intelligence(data-sitekey=process.env.FRC_SITEKEY data-start="auto") - button(type="submit") Log In + button(type="submit" disabled) Log In script(type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.min.js" async defer) script(nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.compat.min.js" async defer) + script. + document.addEventListener("DOMContentLoaded", () => { + const el = document.querySelector(".frc-risk-intelligence"); + const btn = document.querySelector('button[type="submit"]'); + el.addEventListener("frc:riskintelligence.complete", () => { + btn.disabled = false; + }); + el.addEventListener("frc:riskintelligence.error", (e) => { + console.warn("Risk Intelligence token generation errored", e.detail); + btn.disabled = false; + }); + }); ``` If you refresh the page, the token will be generated automatically before the form is focused. ## Retrieving the Risk Intelligence data[​](#retrieving-the-risk-intelligence-data "Direct link to Retrieving the Risk Intelligence data") When the form is submitted, our server-side handler receives a POST request with the username and a Risk Intelligence token. We need to pass that token to the Friendly Captcha API to retrieve the Risk Intelligence data, which will include the browser information. To communicate with the Friendly Captcha API, we’ll use the `@friendlycaptcha/server-sdk` library, which we can install from NPM: ``` npm install @friendlycaptcha/server-sdk ``` With that installed, we can instantiate a `FriendlyCaptchaClient` that we’ll use to retrieve Risk Intelligence data using the token: ``` import express from "express"; import session from "express-session"; +import { FriendlyCaptchaClient } from "@friendlycaptcha/server-sdk"; import * as store from "./store.js"; const app = express(); const port = process.env.PORT || 3000; +const frcClient = new FriendlyCaptchaClient({ + apiKey: process.env.FRC_APIKEY, + sitekey: process.env.FRC_SITEKEY, +}); + ``` Next we can update the `POST /login` handler to extract the Risk Intelligence token from the form: ``` app.post( "/login", express.urlencoded({ extended: false }), async (req, res) => { req.session.user = authenticate(req.body.username); + const browser = await getBrowser(req.body["frc-risk-intelligence-token"]); + console.log(`User ${req.session.user.name} logged in from ${browser || "an unknown browser"}`); req.session.save(() => res.redirect("/")); }, ); ``` We’re now calling a `getBrowser` function to discover the browser used and then logging a message. `getBrowser` encapsulates the Friendly Captcha API call, and the implementation is copied from [the project README](https://github.com/FriendlyCaptcha/friendly-captcha-javascript/?tab=readme-ov-file#retrieving-risk-intelligence) with some minor modifications. Here it is in its entirety: ``` async function getBrowser(token) { if (!token) { return console.warn( "Empty token, skipping Risk Intelligence data retrieval.", ); } const result = await frcClient.retrieveRiskIntelligence(token); // Check if we were able to retrieve the risk intelligence data if (result.wasAbleToRetrieve()) { // Check if the token is valid and data was retrieved successfully if (result.isValid()) { const response = result.getResponse(); const { browser } = response.data.risk_intelligence.client; return `${browser.name}, version ${browser.version}`; } else { // Token was invalid or expired const error = result.getResponseError(); console.log("Error:", error?.error_code, error?.detail); } } else { // Network issue or configuration problem if (result.isClientError()) { console.log("Configuration error - check your API key"); } else { console.log("Network issue or service temporarily unavailable"); } } } ``` Try restarting the server and logging in from a multiple browsers. Here are my server logs: ``` Server listening on port 3000 User friendly logged in from Firefox, version 148 User friendly logged in from Safari, version 17.6 User friendly logged in from Chrome, version 145 ``` **Without a back-end SDK** The implementation above uses the `@friendlycaptcha/server-sdk` library, but you can also send a plain HTTP request. That might look something like this: ``` async function getBrowser(token) { try { const response = await fetch("https://global.frcapi.com/api/v2/riskIntelligence/retrieve", { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": process.env.FRC_APIKEY, }, body: JSON.stringify({ token }), }); const body = await response.text(); const parsed = JSON.parse(body); if (parsed.error) { throw parsed.error; } else if (!response.ok) { throw body; } const { name, version } = parsed.data.risk_intelligence.client.browser; return `${name}, version ${version}`; } catch (error) { console.warn("Failed to retrieve Risk Intelligence data", error); } } ``` ## Risk-based authentication[​](#risk-based-authentication "Direct link to Risk-based authentication") Now that we have the browser information, we can keep track of which browsers we’ve seen for a given user, and perform some special logic if they attempt to log in from a browser we haven’t seen yet. There are a couple of changes we need to make. Let’s start by updating the user model. ``` @@ -7,6 +7,7 @@ export function authenticate(username) { user = { name: username, loginCount: 0, + browsers: new Set(), }; users.set(username, user); } @@ -17,7 +18,18 @@ export function getUser(username) { return users.get(username); } -export function recordLogin(username) { +export function recordLogin(username, browser) { const user = getUser(username); user.loginCount++; + user.browsers.add(browser); +} + +export function shouldConfirm(username, browser) { + const user = getUser(username); + + // There needs to be at least 1 recognized browser + // for there to be an unrecognized browser. + if (user.browsers.size < 1) return false; + + return !user.browsers.has(browser); } ``` Users now have a `Set` to keep track of the browsers they’ve used; when we record a visit from a user, we update the set, and there’s a new function to check if the user needs to confirm the login. Since our risk-based authentication is based on the user logging in from an unrecognized browser, `shouldConfirm` returns true if the current browser is not in the list of previously used browsers. We also need to update the logic in our app’s routes to support the confirmation flow. Let’s start with the `POST /login` handler. ``` app.post( express.urlencoded({ extended: false }), async (req, res) => { const user = store.authenticate(req.body.username); - store.recordLogin(user.name); const browser = await getBrowser(req.body["frc-risk-intelligence-token"]); - console.log( - User ${user.name} logged in from ${browser || "an unknown browser"}, - ); + + let nextRoute; + if (store.shouldConfirm(user.name, browser)) { + // Here you might generate a confirmation code and email it to the user's account, + // but we're going to skip it as part of this tutorial for the sake of simplicity. + nextRoute = "/confirm"; + // Store the browser in the session so we can use it in the POST /confirm route. + req.session.browser = browser; + } else { + nextRoute = "/"; + store.recordLogin(user.name, browser); + } + req.session.username = user.name; - req.session.save(() => res.redirect("/")); + req.session.save(() => res.redirect(nextRoute)); }, ); ``` We’re going to use the new `store.shouldConfirm()` function to branch on whether the user’s browser is recognized or not. If it’s not recognized, we’re redirecting to a new `/confirm` page and storing the browser in the session object (we’ll need it in another handler soon). As mentioned in the comment, a more realistic implementation would generate a confirmation code and email it to the user. If the browser *is* recognized, we record the new login and redirect to the index page. There’s a new `GET /confirm` route that renders the confirmation page. Here’s the tiny handler: ``` app.get("/confirm", (req, res) => { if (!req.session.username) return res.redirect("/login"); res.render("confirm", { title: "Confirm Login", username: req.session.username, }); }); ``` And the template, saved to `views/confirm.pug`: ``` extends layout block content h1 Is that you, #{username}? p(style="width: 60ch") We noticed that you're logging in from a browser we haven't seen before. For your security, we've sent an email with a confirmation code to the address we have on file. Please enter the confirmation code below to complete your login. form(method="post" action="/confirm") label(for="confirmation") Confirmation Code input(type="text" id="confirmation" name="confirmation" required) button(type="submit") Confirm ``` This is what the page looks like in the browser: ![A confirmation form shown when the user is logging in from a new browser](/assets/images/risk-based-auth-confirm-ui-bb6d59c3a4fb55be99f508d5f3a031e5.png) Like the login page, this form accepts any input. Here’s the `POST /confirm` handler that process this form submission: ``` app.post("/confirm", express.urlencoded({ extended: false }), (req, res) => { // The confirmation code is available in req.body.confirmation. // You would compare it to the one you generated in the POST /login handler. store.recordLogin(req.session.username, req.session.browser); res.redirect("/"); }); ``` Upon successful confirmation, we record the login and redirect to the index page. We use the browser that we stored in the session in the `POST /login` handler. With a risk-based authentication flow like this one, your login flow will be more robust against account takeover. There will be no additional friction for users authenticating from their usual browsers. In the event of a credential leak, an attacker would need to match the compromised user’s browser. You can extend this check to [any of the other fields offered by Risk Intelligence](/docs/v2/risk-intelligence/format.md). For example, you might require a confirmation if a user is logging in from a new country or network. To learn more, check out the [**Risk Intelligence**](/docs/v2/risk-intelligence/.md) docs or read through the [**Getting Started**](/docs/v2/risk-intelligence/getting-started/.md) guide. --- # Self-Hosted Endpoint info The Self-Hosted Endpoint feature is only available for Friendly Captcha v2. When a customer website or application loads a Friendly Captcha widget, the widget makes a number of requests to the Friendly Captcha API. The API endpoint is `global.frcapi.com`, or `eu.frcapi.com` for customers who use [the EU Endpoint](/docs/v2/guides/eu-endpoint.md). Friendly Captcha offers the **Self-Hosted Endpoint** feature for customers who prefer to route all end-user traffic through their own infrastructure. To use a Self-Hosted Endpoint, customers operate a proxy server that forwards widget requests to the Friendly Captcha API. Because the Friendly Captcha service depends on data sent by the widget, it is important to ensure that a Self-Hosted Endpoint correctly forwards that data. ## Setup[​](#setup "Direct link to Setup") There are 3 steps to setting up a Self-Hosted Endpoint, explained in detail below. 1. [Generate a proxy key in the Friendly Captcha dashboard.](#1-generate-a-proxy-key) 2. [Configure your web server to proxy widget requests to the Friendly Captcha API.](#2-configure-your-web-server) 3. [Configure your widget to use your Self-Hosted Endpoint.](#3-configure-your-site-or-apps-widget) ### 1. Generate a proxy key[​](#1-generate-a-proxy-key "Direct link to 1. Generate a proxy key") To verify that proxied widget requests come from your infrastructure, you must set a header that contains a proxy key. You can generate a key in the [Friendly Captcha dashboard](https://app.friendlycaptcha.eu/dashboard/accounts/-/keys/proxy). Make sure to generate a **Proxy Key**; API keys are not accepted. Store the generated key somewhere safe and retrievable—Friendly Captcha doesn't keep a copy of the key, so you will have to regenerate it if you lose it. You will use this proxy key in the next step. ### 2. Configure your web server[​](#2-configure-your-web-server "Direct link to 2. Configure your web server") You need to configure your web server to forward the following requests to the Friendly Captcha API: ``` GET /api/v2/captcha/agent GET /api/v2/captcha/widget GET /api/v2/captcha/ping POST /api/v2/captcha/activate POST /api/v2/captcha/quote POST /api/v2/captcha/redeem ``` For these requests, your server should forward the request in its entirety, including all headers. Additionally, the forwarded requests should include two ***extra*** headers: 1. `X-Frc-Proxy-Key`: The proxy key you generated in the Friendly Captcha dashboard. 2. `X-Frc-Proxy-Client-IP`: The original (source) IP address of the end user. Forward the requests to this endpoint (i.e., the upstream server): ``` https://global.proxy.frcapi.com ``` If you have access to [the EU Endpoint](/docs/v2/guides/eu-endpoint.md), you may alternatively forward the requests to this endpoint: ``` https://eu.proxy.frcapi.com ``` [See below](#example-routing-configurations) for examples of how to configure a server to correctly proxy the widget requests. Note If you don't forward the entire request and its headers, the widget may still be functional, but the service will operate in a degraded state. If you forget either of the additional proxy headers (`X-Frc-Proxy-Key` and `X-Frc-Proxy-Client-IP`), the widget will display an error message. ### 3. Configure your site or app's widget[​](#3-configure-your-site-or-apps-widget "Direct link to 3. Configure your site or app's widget") The final step is to configure your server's URL as the API endpoint for your widget. This will ensure that the widget sends its requests to your server (which will then forward them to the upstream Friendly Captcha API). If your server's URL is `https://example.com` and you configure the widget using HTML `data-` attributes, your markup will look something like this: ```
``` If you configure the widget using the JavaScript SDK, your code will look something like this: ``` import { FriendlyCaptchaSDK } from "@friendlycaptcha/sdk" const sdk = new FriendlyCaptchaSDK(); const mount = document.querySelector(".my-widget"); const widget = sdk.createWidget({ element: mount, sitekey: "", apiEndpoint: "https://example.com", }); ``` For more documentation, see the page on [configuring the Friendly Captcha widget](/docs/v2/sdk/configuration.md). ## Example Routing Configurations[​](#example-routing-configurations "Direct link to Example Routing Configurations") Provided below are some example Self-Hosted Endpoints configurations for popular web server technologies. If you copy and paste the configuration, make sure to replace `<% PROXY KEY %>` with your real proxy key, and verify that you're using the correct upstream Friendly Captcha API endpoint. ### Apache[​](#apache "Direct link to Apache") ``` LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule headers_module modules/mod_headers.so RequestHeader set X-Frc-Proxy-Key "<% PROXY KEY %>" RequestHeader set X-Frc-Proxy-Client-IP expr=%{REMOTE_ADDR} ProxyPass https://global.proxy.frcapi.com ProxyPassReverse https://global.proxy.frcapi.com ``` ### Caddy[​](#caddy "Direct link to Caddy") ``` @captcha_paths path_regexp ^/api/v2/captcha/(agent|widget|ping|activate|quote|redeem)(/.*)?$ reverse_proxy @captcha_paths https://global.proxy.frcapi.com { header_up X-Frc-Proxy-Key "<% PROXY KEY %>" header_up X-Frc-Proxy-Client-IP {remote_host} } ``` ### HAProxy[​](#haproxy "Direct link to HAProxy") ``` frontend http-in # other frontend configuration... acl is_captcha_path path_reg ^/api/v2/captcha/(agent|widget|ping|activate|quote|redeem)(/.*)?$ use_backend captcha_paths if is_captcha_path backend captcha_paths mode http http-request set-header X-Frc-Proxy-Key "<% PROXY KEY %>" http-request set-header X-Frc-Proxy-Client-IP %[src] # Note: the path to the certificates file may be different for your OS. server frc_api global.proxy.frcapi.com:443 ssl verify required ca-file /etc/ssl/certs/ca-certificates.crt ``` ### NGINX[​](#nginx "Direct link to NGINX") ``` location ~ ^/api/v2/captcha/(agent|widget|ping|activate|quote|redeem)(/.*)?$ { proxy_set_header X-Frc-Proxy-Key <% PROXY KEY %>; proxy_set_header X-Frc-Proxy-Client-IP $remote_addr; proxy_pass https://global.proxy.frcapi.com; } ``` ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ### Restrictive `X-Frame-Options`[​](#restrictive-x-frame-options "Direct link to restrictive-x-frame-options") The Friendly Captcha widget loads in an `iframe`, which means it will not load if your proxied response includes an `X-Frame-Options: DENY` header. If the widget will not load via your Self-Hosted Endpoint, check that any `X-Frame-Options` headers on the proxied responses are permissive enough to allow the `iframe` to load. You can choose one of the following three options. 1. Don't set the `X-Frame-Options` header at all (this is what the Friendly Captcha API does). 2. Set `X-Frame-Options: SAMEORIGIN` if the page and proxied requests have the same origin. 3. Use the `frame-ancestors` directive of the `Content-Security-Policy` header to specifically allow `iframe`s loaded from your Self-Hosted Endpoint origin. --- # Upgrading from v1 to v2 This guide describes how to upgrade from v1 to v2 of Friendly Captcha. To learn more about the ways v2 improves upon v1, and why upgrading is worth the effort, [**click here**](/docs/v2/versions.md#whats-new-in-v2). ## Changes to your website (front-end changes)[​](#changes-to-your-website-front-end-changes "Direct link to Changes to your website (front-end changes)") ### Script tag installation[​](#script-tag-installation "Direct link to Script tag installation") If you installed Friendly Captcha to your website by adding a ` ``` with the new `@friendlycaptcha/sdk` scripts ``` ``` ## 2. 🇪🇺 Update custom API endpoints[​](#2--update-custom-api-endpoints "Direct link to 2. 🇪🇺 Update custom API endpoints") If you are using a specific endpoint, you need to update the `data-puzzle-endpoint` attribute. The attribute is now called `data-api-endpoint` and it supports shortcuts (`"eu"` for our dedicated EU endpoint). For example, if you are using the dedicated EU-only endpoint, you would replace ```
``` with ```
``` ## 3. Remove `data-lang`[​](#3-remove-data-lang "Direct link to 3-remove-data-lang") Remove the `data-lang="..."` attributes from your widgets. The new v2 widget automatically matches the language on your website. **Example** ```
``` becomes ```
``` If you still want to explicitly force a specific language, replace `data-lang` with `lang`. ## 4. Update your callbacks into event listeners[​](#4-update-your-callbacks-into-event-listeners "Direct link to 4. Update your callbacks into event listeners") If specify `data-callback`, `data-error-callback` or `data-expired-callback` on your widget, these need to be replaced with event handlers. If you are currently using these callbacks to enable and disable a submit button, your code may look like this: ```
``` ``` ``` You would replace it with the following ```
``` ``` ``` For more information around the events and possible states, see [the Events documentation](/docs/v2/sdk/events.md). ## 5. Next steps[​](#5-next-steps "Direct link to 5. Next steps") With these changes the widget should function the same way as it did before. You will need to make some changes to your backend server code next, which are described in [this guide](/docs/v2/guides/upgrading-from-v1/backend-integration.md). --- # Captcha Warning Friendly Captcha may display a warning message that indicates billing problems or other misconfigurations. This page explains what each warning means and how to resolve it. Not the website administrator? These warnings are **only relevant to the website administrator**. If you're a visitor trying to use a website that shows a Friendly Captcha widget, you can **ignore the warning** and continue using the site normally. The warning is not related to anything you've done. If you are the website administrator, you should **fix the warning as soon as possible or reach out to us to avoid service disruption**. ## Usage limit reached[​](#usage_limit_reached "Direct link to Usage limit reached") ![Screenshot of the Friendly Captcha widget warning for usage limit reached](/assets/images/widget-warning-overusage-5b24be32f0864b9c9cc07aeac7cbe968.png) Your Friendly Captcha account has reached its monthly usage limit. The widget will continue to work and users will be able to complete the captcha, but you will see a warning until the issue is resolved. If the issue is not resolved, the widget may stop working entirely until you upgrade your plan. **To fix this:** * Check your usage in the [Friendly Captcha dashboard](https://app.friendlycaptcha.eu/dashboard/). * Upgrade to a plan that offers more usage. If you are having trouble upgrading your plan or need help, please [contact support](https://friendlycaptcha.com/support/). ## Commercial use detected[​](#invalid_non_commercial_use "Direct link to Commercial use detected") ![Screenshot of the Friendly Captcha widget warning for commercial use](/assets/images/widget-warning-commercial-use-46cc066279f3e9c169c5ff29c59941af.png) Your account is on the free plan, which is for non-commercial use only. Since you were previously on a paid plan, you likely need a commercial license. The widget will continue to work and users will be able to complete the captcha, but you will see a warning until the issue is resolved. If the issue is not resolved, the widget may stop working entirely until you upgrade your plan. **To fix this:** * Open the [Friendly Captcha dashboard](https://app.friendlycaptcha.eu/dashboard/). * Upgrade to a paid plan to get a commercial license and remove the warning. If you believe your use qualifies as non-commercial, or you need help choosing a plan, please [contact support](https://friendlycaptcha.com/support/). --- # Risk Intelligence *Risk Intelligence* helps make decisions about the trustworthiness of users interacting with your services. Friendly Captcha provides Risk Intelligence data for your website users without any friction or user interaction. This data includes various risk scores and signals that can help you assess the likelihood of fraudulent or malicious activity. You can store and use this data to enhance your (existing) security systems, such as implementing risk-based authentication or fraud detection systems. For example, you may want to flag users with high risk scores for additional verification steps. Risk Intelligence is available automatically for all customers on **Advanced** and **Enterprise** plans. ## How do I get Risk Intelligence data?[​](#how-do-i-get-risk-intelligence-data "Direct link to How do I get Risk Intelligence data?") The primary way to get Risk Intelligence data is through the Risk Intelligence API. This allows you to generate a risk intelligence token in the frontend and send it to your backend to retrieve the data. Follow the [**Getting Started**](/docs/v2/risk-intelligence/getting-started/.md) guide to learn how to use the Risk Intelligence API. ### Risk Intelligence on Captcha Challenge[​](#risk-intelligence-on-captcha-challenge "Direct link to Risk Intelligence on Captcha Challenge") Alternatively, you can get Risk Intelligence data when verifying a captcha challenge using the [siteverify API](/docs/v2/api/siteverify.md). This is a paid add-on that can be requested by [contacting support](https://friendlycaptcha.com/support/). You can read more about this in our [Risk Intelligence on Captcha Challenge](/docs/v2/risk-intelligence/on-captcha-challenge.md) document. ## Why is Risk Intelligence useful?[​](#why-is-risk-intelligence-useful "Direct link to Why is Risk Intelligence useful?") We wrote up example use cases for Risk Intelligence data in the [**Use Cases**](/docs/v2/risk-intelligence/use-cases.md) document. In short, Risk Intelligence allows for more control and flexibility in how you handle user interactions for your applications. Perhaps low-risk users can interact with your services without friction, while high-risk users are required to complete additional verification steps. Additionally, you may want to collect additional data about high-risk users for auditing and monitoring purposes, or feed it into your existing fraud detection systems. ## What data is included in Risk Intelligence?[​](#what-data-is-included-in-risk-intelligence "Direct link to What data is included in Risk Intelligence?") Risk Intelligence consists of five modules, each providing different types of risk insights. * **Risk Scores**: Combined risk scores that summarize the risk in different categories such as browser risk and network risk. * **IP Intelligence**: Information about the IP address of the user, such as geolocation, ASN, and ASN type such as residential vs datacenter. IP Intelligence is sourced from [IP Trust](https://iptrust.co), a Friendly Captcha product. * **Anonymization Detection**: Signals that indicate whether the user is using anonymization or masking services like VPNs or proxies. *This module will be launched in Q2 2026.* * **Browser Identification**: Signals that help identify the browser and device being used by the user. * **Bot Detection**: Information about what kind of automation or bot was detected. ## Risk Intelligence data format[​](#risk-intelligence-data-format "Direct link to Risk Intelligence data format") The Risk Intelligence data is returned as a JSON object in the `risk_intelligence` field of the API response. Below is an example of what the data might look like: ``` { // Risk scores summarizing the assessment into scores per category (1-5) (Risk Scores module) risk_scores: { overall: 2, // Overall risk score combining all factors network: 2, // Network-related risk based on IP, ASN, reputation, geolocation browser: 1 // Browser-related risk based on user agent, automation traces, consistency }, network: { ip: "88.64.123.45", // IP address of the request, note this plain IP is *never* stored on our servers // Autonomous System information (IP Intelligence module) as: { asn: 3209, // Autonomous System Number name: "VODANET", // AS name/handle company: "Vodafone GmbH", // Organization name that owns the ASN description: "Provides mobile and fixed broadband and telecommunication services to consumers and businesses.", domain: "vodafone.de", // Domain associated with the ASN country: "DE", // Two-letter country code where ASN is registered rir: "RIPE", // Regional Internet Registry that allocated the ASN route: "88.64.0.0/12", // IP route in CIDR notation type: "isp" // AS type (isp, hosting, mobile, etc.) }, // Geographic location of the IP (IP Intelligence module) geolocation: { country: { iso2: "DE", // Two-letter ISO 3166-1 alpha-2 code iso3: "DEU", // Three-letter ISO 3166-1 alpha-3 code name: "Germany", // Country name in English name_native: "Deutschland", // Country name in native language region: "Europe", // Major world region subregion: "Western Europe", // More specific world region currency: "EUR", // ISO 4217 currency code currency_name: "Euro", // Full name of the currency phone_code: "49", // International dialing code capital: "Berlin" // Capital city }, city: "Eschborn", // City name (empty string if unknown) state: "Hessen" // State/region/province (empty string if unknown) }, // Abuse contact information (IP Intelligence module) abuse_contact: { address: "Vodafone GmbH, Campus Eschborn, Duesseldorfer Strasse 15, D-65760 Eschborn, Germany", // Postal address name: "Vodafone Germany IP Core Backbone", // Abuse contact name email: "abuse.de@vodafone.com", // Abuse contact email phone: "+49 6196 52352105" // Abuse contact phone }, // IP anonymization detection (Anonymization Detection module) anonymization: { vpn_score: 2, // VPN likelihood score (1-5) proxy_score: 1, // Proxy likelihood score (1-5) tor: false, // Tor exit node detected icloud_private_relay: false // iCloud Private Relay detected } }, client: { // User-Agent HTTP header sent by the browser header_user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0", // Time zone from the browser (available when Browser Identification module is enabled) time_zone: { name: "Europe/Berlin", // IANA time zone identifier country_iso2: "DE" // Country derived from time zone }, // Detected browser information (Browser Identification module) browser: { id: "firefox", // Browser identifier name: "Firefox", // Human-readable browser name version: "146.0", // Browser version release_date: "2026-01-28" // Release date of this version (YYYY-MM-DD) }, // Browser rendering engine (Browser Identification module) browser_engine: { id: "gecko", // Engine identifier name: "Gecko", // Human-readable engine name version: "146.0" // Engine version }, // Device information (Browser Identification module) device: { type: "desktop", // Device type (desktop, mobile, tablet, etc.) brand: "", // Device manufacturer brand (empty for desktop) model: "" // Device model name (empty for desktop) }, // Operating system information (Browser Identification module) os: { id: "windows", // OS identifier name: "Windows", // Human-readable OS name version: "10" // OS version }, // TLS/SSL signatures (Bot Detection module) tls_signature: { ja3: "d87a30a5782a73a83c1544bb06332780", // JA3 hash ja3n: "28ecc2d2875b345cecbb632b12d8c1e0", // JA3N hash (normalized) ja4: "t13d1516h2_8daaf6152771_02713d6af862" // JA4 signature }, // Automation and bot detection (Bot Detection module) automation: { // Automation tool detection automation_tool: { detected: false, // Whether automation tool was detected id: "", // Tool identifier (puppeteer, selenium, etc.) name: "", // Human-readable tool name type: "" // Tool type (webdriver, cdp, etc.) }, // Known bot/crawler detection (good bots with public documentation) known_bot: { detected: false, // Whether a known bot was detected id: "", // Bot identifier (googlebot, bingbot, etc.) name: "", // Human-readable bot name type: "", // Bot type (search, monitor, social, etc.) url: "" // URL to bot documentation } } } } ``` The entire format is documented on the [Risk Intelligence Format](/docs/v2/risk-intelligence/format.md) page, where you can find detailed descriptions of all fields and example values. ## Next steps[​](#next-steps "Direct link to Next steps") If you want to use Risk Intelligence today, you can follow the [**Getting Started**](/docs/v2/risk-intelligence/getting-started/.md) guide. If you want to learn more about how to use the Risk Intelligence data, check out the [**Use Cases**](/docs/v2/risk-intelligence/use-cases.md) document where we provide example implementations and ideas for how to use this data to enhance your security. --- # Format Risk Intelligence data is returned in a structured JSON format that includes various risk signals and scores. The exact structure of the data may vary depending on which modules you have enabled on your account (as of writing, all modules are included by default). info The structure of the Risk Intelligence data is subject to change. We always aim for backwards compatibility, but your validation and parsing logic should be flexible enough to handle new fields being added in the future. ## Structure Overview[​](#structure-overview "Direct link to Structure Overview") The Risk Intelligence data is organized into three high level sections: ``` { risk_scores: { ... }, // Overall risk scores per category (1-5). network: { ... }, // Information about the user's network and IP address. client: { ... }, // Detected browser or bot information. } ``` ### Risk Scores[​](#risk-scores "Direct link to Risk Scores") The `risk_scores` section provides a summary of the risk assessment in the form of scores for different categories. Each score is an integer value between 1 and 5, where 1 indicates very low risk and 5 indicates very high risk. The available risk scores include: * `overall`: An overall risk score that combines all available signals into a single score. * `network`: A risk score based on network-related signals such as IP reputation, ASN information, and geolocation. You can interpret this score as, based on the user's network characteristics, how likely the request is to be automated, fraudulent or malicious. * `browser`: A risk score based on browser-related signals such as user agent, browser identification, bot identification and other client-side characteristics. This score helps assess the likelihood of the request being automated or coming from a suspicious client. JSON Example ``` "risk_scores": { "overall": 2, "network": 2, "browser": 1 } ``` ### Network Information[​](#network-information "Direct link to Network Information") info The IP address is never stored on our servers in an unhashed format. We encode it into the risk intelligence token so that we can pass it on to you. You can compare this IP address to the one you see in your server logs to correlate the risk intelligence data with specific requests. The `network` section provides detailed information about the user's network and IP address. This includes: * `ip`: The IP address of the user when the risk intelligence data was gathered on the frontend. * `as`: Information about the Autonomous System (AS) associated with the user's IP address, including ASN, AS name, company, description, domain, country, RIR, route and type. * `geolocation`: Geographic information about the user's IP address, including city, region, country. * `anonymization`: Signals indicating whether the user is using anonymization services such as VPNs or proxies, including VPN detection, proxy detection, Tor detection. JSON Example ``` "network": { "ip": "88.64.123.45" "as": { "asn": 3209, "name": "VODANET", "company": "Vodafone GmbH", "description": "Provides mobile and fixed broadband and telecommunication services to consumers and businesses.", "domain": "vodafone.de", "country": "DE", "rir": "RIPE", "route": "88.64.0.0/12", "type": "isp" }, "geolocation": { "country": { "iso2": "DE", "iso3": "DEU", "name": "Germany", "name_native": "Deutschland", "region": "Europe", "subregion": "Western Europe", "currency": "EUR", "currency_name": "Euro", "phone_code": "49", "capital": "Berlin" }, "city": "Eschborn", "state": "Hessen" }, "anonymization": { "vpn_score": 2, "proxy_score": 1, "tor": false, "icloud_private_relay": false } } ``` ### Client Information[​](#client-information "Direct link to Client Information") The `client` section provides information about the user's client, which can be a browser or a bot. This includes: * `browser`: Detected browser information, including browser name, version, rendering engine, device type, brand and model, and operating system. * `tls_signature`: TLS/SSL-derived information, including JA3 and JA4 signatures. These signatures can be used to identify specific clients and detect anomalies. * `automation`: Signals related to automation and bot detection, including detected automation tools and bot types. JSON Example ``` "client": { "header_user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0", "time_zone": { "name": "Europe/Berlin", "country_iso2": "DE" }, "browser": { "id": "firefox", "name": "Firefox", "version": "146.0", "release_date": "2026-01-28" }, "browser_engine": { "id": "gecko", "name": "Gecko", "version": "146.0" }, "device": { "type": "desktop", "brand": "", "model": "" }, "os": { "id": "windows", "name": "Windows", "version": "10" }, "tls_signature": { "ja3": "d87a30a5782a73a83c1544bb06332780", "ja3n": "28ecc2d2875b345cecbb632b12d8c1e0", "ja4": "t13d1516h2_8daaf6152771_02713d6af862" }, "automation": { "automation_tool": { "detected": false, "id": "", "name": "", "type": "" }, "known_bot": { "detected": false, "id": "", "name": "", "type": "", "url": "" } } } ``` ## Field Reference[​](#field-reference "Direct link to Field Reference") A comprehensive breakdown of all fields in the Risk Intelligence data format. ### Risk Scores[​](#risk-scores-1 "Direct link to Risk Scores") `risk_scores` - Risk assessment scores Risk scores summarize the entire risk intelligence assessment into scores per category. Each score is an integer from 0-5. | Value | Meaning | | ----- | ------------------ | | `0` | Unknown or missing | | `1` | Very low risk | | `2` | Low risk | | `3` | Medium risk | | `4` | High risk | | `5` | Very high risk | info **Why are risk scores not a percentage or a decimal score?** We use a 1-5 integer scale to make it easier for you to set thresholds and make decisions based on the scores. A percentage or decimal score can give a false sense of precision, while in reality the underlying data and signals are often noisy and not precise enough to justify that level of granularity (ie what is the difference between 81% and 82% risk?). The 1-5 scale provides a more actionable and interpretable way to use the risk scores in your decision-making processes. `overall` - Overall risk score **Type:** Integer (0-5)
**Description:** A combined risk score that aggregates all available signals into a single assessment. **Example:** ``` "overall": 2 ``` `network` - Network risk score **Type:** Integer (0-5)
**Description:** Risk score based on network-related signals such as IP reputation, ASN information, geolocation, past abuse from this network, and other network characteristics. Indicates the likelihood of automation or malicious activity based on the network. **Example:** ``` "network": 2 ``` `browser` - Browser risk score **Type:** Integer (0-5)
**Description:** Risk score based on browser-related signals such as user agent consistency, automation traces, past abuse, and browser characteristics. Indicates the likelihood of automation, malicious activity, or browser spoofing. **Example:** ``` "browser": 1 ``` ### Network[​](#network "Direct link to Network") `network` - Network and IP information Contains information about the user's network, IP address, and related characteristics. `ip` - Client IP address **Type:** String
**Description:** The IP address used when requesting the challenge. You can compare this IP with the one that is submitting to your backend. Note that IP addresses can change mid-session, for example when users are on mobile networks or using certain ISPs - so do create a way for users to update the IP address associated with a session in your system. **Example:** ``` "ip": "88.64.4.22" ``` `as` - Autonomous System information **Type:** Object or `null`
**Description:** Information about the Autonomous System that owns the IP address. `number` - ASN **Type:** Integer
**Description:** Autonomous System Number (ASN) identifier. **Example:** ``` "number": 3209 ``` `name` - AS name **Type:** String
**Description:** Short name or handle of the autonomous system. **Example:** ``` "name": "VODANET" ``` `company` - Company name **Type:** String
**Description:** Organization name that owns the ASN. **Example:** ``` "company": "Vodafone GmbH" ``` `description` - Company description **Type:** String
**Description:** Short description of the company that owns the ASN. **Example:** ``` "description": "Provides mobile and fixed broadband and telecommunication services to consumers and businesses." ``` `domain` - Company domain **Type:** String
**Description:** Domain name associated with the ASN. **Example:** ``` "domain": "vodafone.de" ``` `country` - ASN country **Type:** String
**Description:** Two-letter ISO 3166-1 alpha-2 country code where the ASN is registered. **Example:** ``` "country": "DE" ``` `rir` - Regional Internet Registry **Type:** String
**Description:** RIR that allocated the ASN. **Possible Values:** * `"ARIN"` - American Registry for Internet Numbers (North America) * `"RIPE"` - Réseaux IP Européens (Europe, Middle East, Central Asia) * `"APNIC"` - Asia-Pacific Network Information Centre * `"LACNIC"` - Latin America and Caribbean Network Information Centre * `"AFRINIC"` - African Network Information Centre **Example:** ``` "rir": "RIPE" ``` `route` - IP route **Type:** String
**Description:** IP route in CIDR notation associated with the ASN. **Example:** ``` "route": "88.64.0.0/12" ``` `type` - AS type **Type:** String
**Description:** Type classification of the autonomous system. **Possible Values:** * `"isp"` - Internet Service Provider * `"mobile"` - Mobile network operator * `"government"` - Government organization * `"hosting"` - Hosting/data center provider * `"education"` - Educational institution * `"individual"` - Individual/personal ASN * `"business"` - Business/corporate network * `"other"` - Other/uncategorized **Example:** ``` "type": "isp" ``` `geolocation` - Geographic location **Type:** Object or `null`
**Description:** Geographic location information for the IP address. `country` - Country information **Type:** Object
**Description:** Detailed information about the country. `iso2` - ISO 3166-1 alpha-2 code **Type:** String
**Description:** Two-letter country code. **Example:** ``` "iso2": "DE" ``` `iso3` - ISO 3166-1 alpha-3 code **Type:** String
**Description:** Three-letter country code. **Example:** ``` "iso3": "DEU" ``` `name` - Country name **Type:** String
**Description:** English name of the country. **Example:** ``` "name": "Germany" ``` `name_native` - Native country name **Type:** String
**Description:** Country name in its native language. **Example:** ``` "name_native": "Deutschland" ``` `region` - Geographic region **Type:** String
**Description:** Major world region. **Example:** ``` "region": "Europe" ``` `subregion` - Geographic subregion **Type:** String
**Description:** More specific world region. **Example:** ``` "subregion": "Western Europe" ``` `currency` - Currency code **Type:** String
**Description:** ISO 4217 currency code. **Example:** ``` "currency": "EUR" ``` `currency_name` - Currency name **Type:** String
**Description:** Full name of the primary currency used in the country. **Example:** ``` "currency_name": "Euro" ``` `phone_code` - Country phone code **Type:** String
**Description:** International dialing code. **Example:** ``` "phone_code": "49" ``` `capital` - Capital city **Type:** String
**Description:** Name of the capital city. **Example:** ``` "capital": "Berlin" ``` `city` - City name **Type:** String
**Description:** City of the IP address. Empty string if unknown. **Example:** ``` "city": "Eschborn" ``` `state` - State/region/province **Type:** String
**Description:** State, region, or province of the IP address. Empty string if unknown. **Example:** ``` "state": "Hessen" ``` `abuse_contact` - Abuse contact information **Type:** Object or `null`
**Description:** Contact information for reporting network abuse. `address` - Postal address **Type:** String
**Description:** Postal address of the abuse contact. **Example:** ``` "address": "Vodafone GmbH, Campus Eschborn, Duesseldorfer Strasse 15, D-65760 Eschborn, Germany" ``` `name` - Contact name **Type:** String
**Description:** Name of the abuse contact person or team. **Example:** ``` "name": "Vodafone Germany IP Core Backbone" ``` `email` - Contact email **Type:** String
**Description:** Abuse contact email address. **Example:** ``` "email": "abuse.de@vodafone.com" ``` `phone` - Contact phone **Type:** String
**Description:** Abuse contact phone number. This can be in various formats, but often includes the country code. Note that not all abuse contacts provide a phone number. **Example:** ``` "phone": "+49 6196 52352105" ``` `anonymization` - Anonymization detection **Type:** Object or `null`
**Description:** Detection of VPNs, proxies, and anonymization services. `vpn_score` - VPN detection score **Type:** Integer (0-5)
**Description:** Likelihood that a VPN is being used. Higher scores indicate stronger evidence of VPN usage. **Example:** ``` "vpn_score": 2 ``` `proxy_score` - Proxy detection score **Type:** Integer (0-5)
**Description:** Likelihood that the user is connecting through a proxy server. Higher scores indicate stronger evidence of proxy usage. **Example:** ``` "proxy_score": 1 ``` `tor` - Tor exit node **Type:** Boolean
**Description:** Whether the IP is a Tor exit node. **Example:** ``` "tor": false ``` `icloud_private_relay` - iCloud Private Relay **Type:** Boolean
**Description:** Whether the IP is from iCloud Private Relay. **Example:** ``` "icloud_private_relay": false ``` ### Client[​](#client "Direct link to Client") `client` - User agent and device information Contains information about the client device, browser, and operating system. `header_user_agent` - User-Agent header **Type:** String
**Description:** The User-Agent HTTP header value. **Example:** ``` "header_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0" ``` `time_zone` - Time zone information **Type:** Object or `null`
**Description:** IANA time zone information from the browser. `name` - IANA time zone **Type:** String
**Description:** IANA time zone name reported by the browser. **Example:** ``` "name": "America/New_York" ``` `country_iso2` - Country from time zone **Type:** String
**Description:** Two-letter ISO 3166-1 alpha-2 country code derived from the time zone. Returns `"XU"` if timezone is missing or cannot be mapped to a country (e.g., "Etc/UTC"). **Example:** ``` "country_iso2": "US" ``` `browser` - Browser information **Type:** Object or `null`
**Description:** Detected browser details. `id` - Browser identifier **Type:** String
**Description:** Unique browser identifier. Empty string if browser could not be identified. **Possible Values:** * `"chrome"` - Chrome * `"chrome_android"` - Chrome for Android * `"edge"` - Microsoft Edge * `"firefox"` - Firefox * `"firefox_android"` - Firefox for Android * `"ie"` - Internet Explorer * `"oculus"` - Quest Browser (Oculus) * `"opera"` - Opera * `"opera_android"` - Opera for Android * `"safari"` - Safari * `"safari_ios"` - Safari on iOS * `"samsunginternet_android"` - Samsung Internet for Android * `"webview_android"` - WebView on Android * `"webview_ios"` - WebView on iOS * `""` - Unknown **Example:** ``` "id": "firefox" ``` `name` - Browser name **Type:** String
**Description:** Human-readable browser name. Empty string if browser could not be identified. **Example:** ``` "name": "Firefox" ``` `version` - Browser version **Type:** String
**Description:** Browser version number. Assumed to be the most recent release matching the signature if exact version unknown. Empty if unknown. **Example:** ``` "version": "146.0" ``` `release_date` - Version release date **Type:** String
**Description:** Release date of the browser version in "YYYY-MM-DD" format. Empty string if unknown. **Example:** ``` "release_date": "2026-01-28" ``` `browser_engine` - Rendering engine information **Type:** Object or `null`
**Description:** Browser rendering engine details. `id` - Engine identifier **Type:** String
**Description:** Unique rendering engine identifier. Empty string if engine could not be identified. **Possible Values:** * `"blink"` - Blink (Chromium-based browsers like Chrome, Edge, Opera) * `"edgehtml"` - EdgeHTML (legacy Microsoft Edge, no longer developed) * `"gecko"` - Gecko (Firefox) * `"presto"` - Presto (legacy Opera, no longer developed) * `"trident"` - Trident (Internet Explorer) * `"v8"` - V8 JavaScript engine * `"webkit"` - WebKit (Safari) * `""` - Unknown **Example:** ``` "id": "gecko" ``` `name` - Engine name **Type:** String
**Description:** Human-readable engine name. Empty string if engine could not be identified. **Example:** ``` "name": "Gecko" ``` `version` - Engine version **Type:** String
**Description:** Rendering engine version. Assumed to be the most recent release matching the signature if exact version unknown. Empty if unknown. **Example:** ``` "version": "146.0" ``` `device` - Device information **Type:** Object or `null`
**Description:** Device type and details. `type` - Device type **Type:** String
**Description:** Type of device. **Possible Values:** * `"desktop"` - Desktop device * `"mobile"` - Mobile phone * `"tablet"` - Tablet device * `"tv"` - TV device * `"console"` - Game console (PlayStation, Xbox, etc.) * `"wearable"` - Wearable device (smartwatch, fitness tracker, etc.) * `"xr"` - Extended reality device (VR headset, AR glasses, etc.) * `"unknown"` - Unknown device type **Example:** ``` "type": "desktop" ``` `brand` - Device brand **Type:** String
**Description:** Manufacturer brand. **Example:** ``` "brand": "Apple" ``` `model` - Device model **Type:** String
**Description:** Device model name. **Example:** ``` "model": "iPhone 17" ``` `os` - Operating system information **Type:** Object or `null`
**Description:** Detected operating system details. `id` - OS identifier **Type:** String
**Description:** Unique operating system identifier. Empty string if OS could not be identified. **Possible Values:** * `"windows"` - Microsoft Windows * `"macos"` - Apple macOS * `"linux"` - Linux * `"android"` - Android * `"ios"` - Apple iOS * `"ipados"` - Apple iPadOS * `"chromeos"` - ChromeOS * `""` - Unknown **Example:** ``` "id": "windows" ``` `name` - OS name **Type:** String
**Description:** Human-readable operating system name. Empty string if OS could not be identified. **Example:** ``` "name": "Windows" ``` `version` - OS version **Type:** String
**Description:** Operating system version number. **Example:** ``` "version": "10" ``` `tls_signature` - TLS/SSL signatures **Type:** Object or `null`
**Description:** TLS client hello signatures (also called TLS fingerprints) derived from the TLS handshake between the client and server. `ja3` - JA3 signature **Type:** String
**Description:** JA3 TLS fingerprint hash. **Example:** ``` "ja3": "d87a30a5782a73a83c1544bb06332780" ``` `ja3n` - JA3N signature **Type:** String
**Description:** JA3N TLS fingerprint hash. **Example:** ``` "ja3n": "28ecc2d2875b345cecbb632b12d8c1e0" ``` `ja4` - JA4 signature **Type:** String
**Description:** JA4 TLS fingerprint. **Example:** ``` "ja4": "t13d1516h2_8daaf6152771_02713d6af862" ``` `automation` - Automation and bot detection **Type:** Object or `null`
**Description:** Information about detected automation and bots. `automation_tool` - Automation tool detection **Type:** Object
**Description:** Detected automation tool information. Note that many automation tools are designed to mimic real browsers and may not be detected, so a value of `detected: false` does not necessarily mean that no automation is being used. The browser risk score can help assess the likelihood of automation even when specific tools are not detected. `detected` - Detection flag **Type:** Boolean
**Description:** Whether an automation tool was detected. **Example:** ``` "detected": false ``` `id` - Tool identifier **Type:** String
**Description:** Automation tool identifier. Empty if no tool detected. **Examples:** `"puppeteer"`, `"playwright"`, `"webdriver"` **Example:** ``` "id": "playwright" ``` `name` - Tool name **Type:** String
**Description:** Human-readable tool name. Empty if no tool detected. **Examples:** `"Puppeteer"`, `"Playwright"`, `"WebDriver"` **Example:** ``` "name": "Playwright" ``` `type` - Tool type **Type:** String
**Description:** Type of automation tool. Empty if no tool detected. **Possible Values:** * `"browser_automation"` - Browser automation tool (e.g., Puppeteer, WebDriver, Playwright) * `""` - No tool detected Note **Example:** ``` "type": "browser_automation" ``` `known_bot` - Known bot detection **Type:** Object
**Description:** Detected known bot information. Known bots have public documentation about their identity and purpose. `detected` - Detection flag **Type:** Boolean
**Description:** Whether a known bot was detected. **Example:** ``` "detected": false ``` `id` - Bot identifier **Type:** String
**Description:** Bot identifier. Empty if no bot detected. **Possible Values (Search Engines):** * `"GoogleBot"` - Google search crawler * `"BingBot"` - Microsoft Bing search crawler * `"YahooBot"` - Yahoo search crawler * `"DuckDuckBot"` - DuckDuckGo search crawler * `"BaiduBot"` - Baidu search crawler (China) * `"YandexBot"` - Yandex search crawler (Russia) * `"360Bot"` - 360 search crawler (China) * `"SogouBot"` - Sogou search crawler (China) * `"SeznamBot"` - Seznam search crawler (Czech Republic) **Possible Values (Social Media):** * `"FacebookBot"` - Facebook link preview crawler * `"TwitterBot"` - Twitter/X link preview crawler * `"LinkedInBot"` - LinkedIn link preview crawler * `"PinterestBot"` - Pinterest crawler * `"DiscordBot"` - Discord link preview bot * `"TelegramBot"` - Telegram link preview bot * `"WhatsAppBot"` - WhatsApp link preview bot **Possible Values (AI/LLM):** * `"OAI-SearchBot"` - OpenAI web search bot * `"GPTBot"` - OpenAI GPT crawler * `"ChatGPT-User"` - ChatGPT user-initiated request * `"ClaudeBot"` - Anthropic Claude crawler * `"PerplexityBot"` - Perplexity AI crawler * `"Perplexity-User"` - Perplexity user-initiated request * `"Google-Extended"` - Google AI/LLM crawler * `"Applebot-Extended"` - Apple AI/LLM crawler **Possible Values (SEO/Crawlers):** * `"SemrushBot"` - Semrush SEO crawler * `"AhrefsBot"` - Ahrefs SEO crawler * `"MJ12Bot"` - Majestic SEO crawler * `"DotBot"` - Moz/OpenSiteExplorer crawler * `"RogerBot"` - Moz/RogerBot crawler * `"PetalBot"` - Huawei Petal search crawler * `"ExaBot"` - Exalead crawler * `"ProximicBot"` - Comscore Proximic crawler **Possible Values (Monitoring):** * `"UptimeRobot"` - Uptime monitoring service * `"PingdomBot"` - Pingdom monitoring service * `"GTmetrixBot"` - GTmetrix performance monitoring * `"Site24x7Bot"` - Site24x7 monitoring service * `"NewRelicBot"` - New Relic monitoring service * `"MonitisBot"` - Monitis monitoring service **Possible Values (Other):** * `"AppleBot"` - Apple web crawler * `"AmazonBot"` - Amazon web crawler * `"MSNBot"` - Microsoft MSN crawler (legacy) * `"InternetArchiveBot"` - Internet Archive Wayback Machine crawler * `"UnknownBot"` - Detected as bot but specific identity unknown * `""` - No bot detected **Example:** ``` "id": "GoogleBot" ``` `name` - Bot name **Type:** String
**Description:** Human-readable bot name. Empty if no bot detected. **Examples:** `"Googlebot"`, `"Bingbot"`, `"ChatGPT"` **Example:** ``` "name": "" ``` `type` - Bot type **Type:** String
**Description:** Bot type classification. Empty if no bot detected. **Possible Values:** * `"search_engine"` - Search engine crawler * `"social"` - Social media crawler/link preview bot * `"crawler"` - General web crawler (SEO, archival, etc.) * `"monitoring"` - Monitoring/uptime check service * `"ai"` - AI bot (generic, when specific type unknown) * `"ai_crawler"` - AI model training/data collection crawler * `"ai_user_initiated"` - AI assistant responding to user request * `"ai_agent"` - Autonomous AI agent * `""` - No bot detected **Example:** ``` "type": "search_engine" ``` `url` - Bot documentation URL **Type:** String
**Description:** Link to bot documentation. Empty if no bot detected. **Example:** ``` "url": "https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers" ``` --- # Getting Started with Risk Intelligence There are two parts to getting Risk Intelligence data. First, you generate a Risk Intelligence ***token*** in your project's front-end. Second, you use that token to retrieve Risk Intelligence ***data*** from the Friendly Captcha API via your project's back-end. ![The Risk Intelligence request flow](/assets/images/flow-b198033155eb825bd0f250fe83e9685f.svg) The request flow for Risk Intelligence, showing token generation and data retrieval. With the data in your project's back-end, you can make Risk Intelligence-informed decisions about how to handle user actions in your project---e.g., require another authentication step, or send a suspicious account activity email. To get a sense of some possibilities, take a look at [the list of use cases](/docs/v2/risk-intelligence/use-cases.md). To get started, follow these steps: 1. [Create a sitekey and API key.](/docs/v2/risk-intelligence/getting-started/setup.md) 2. [Generate a Risk Intelligence token in your project's front-end.](/docs/v2/risk-intelligence/getting-started/generate.md) 3. [Retrieve Risk Intelligence data in your project's back-end.](/docs/v2/risk-intelligence/getting-started/retrieve.md) --- # Generate a Risk Intelligence token You can generate a token directly using [JavaScript](#javascript), or you can use an [HTML element](#html) to automatically generate a token and embed it in a `
` element. *** ## JavaScript[​](#javascript "Direct link to JavaScript") ### Install the library[​](#install-the-library "Direct link to Install the library") ``` npm install @friendlycaptcha/sdk ``` ### Generate the token[​](#generate-the-token "Direct link to Generate the token") ``` import { FriendlyCaptchaSDK } from "@friendlycaptcha/sdk"; // You only need to instantiate one SDK object. const sdk = new FriendlyCaptchaSDK(); const data = await sdk.riskIntelligence({ sitekey: "", }); // `data` is an object with `token` and `expiresAt` properties. ``` ### Configuration[​](#configuration "Direct link to Configuration") You can configure the call to `sdk.riskIntelligence()` with a few different parameters. For details, see the [SDK reference](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.riskintelligence.md). *** ## HTML[​](#html "Direct link to HTML") You can use the same *`site`* script that you use for installing Friendly Captcha widgets to automatically generate Risk Intelligence tokens. The script searches for HTML elements with the `frc-risk-intelligence` class and generates tokens for each element, embedding them in a hidden `` element if they are located within a `` element. ### Add the scripts[​](#add-the-scripts "Direct link to Add the scripts") Include these two scripts in your HTML. ``` ``` Using the scripts without a CDN (i.e. self-hosting) Using `cdn.jsdelivr.net` is optional. If preferred, you can self-host the scripts. [Download the latest release files](https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0) and serve them from your own server. Remember to update these scripts regularly. `cdn.jsdelivr.net` is blocked in some jurisdictions, like some parts of China. If your website needs to be reachable from these jurisdictions, we recommend that you self-host the scripts. ### Include the elements[​](#include-the-elements "Direct link to Include the elements") Insert HTML elements with a class of `frc-risk-intelligence` inside a ``, and tokens will automatically be generated and embedded within hidden `` elements in the form. ```
``` When the form is submitted, the request will include a Risk Intelligence token called `frc-risk-intelligence-token` (though this name can be configured). ### Configuration[​](#configuration-1 "Direct link to Configuration") You can configure the Risk Intelligence call by setting `data-*` attributes on the HTML element. #### `data-sitekey`[​](#data-sitekey "Direct link to data-sitekey") **Required**. The sitekey for your Friendly Captcha application. See [the previous step](/docs/v2/risk-intelligence/getting-started/setup.md#sitekey) for instructions on creating one. Starts with `FC`. ##### Example[​](#example "Direct link to Example") ```
``` #### `data-api-endpoint`[​](#data-api-endpoint "Direct link to data-api-endpoint") *Only relevant if you are using our [dedicated EU endpoint service](/docs/v2/guides/eu-endpoint.md).* By default the API is served from `https://global.frcapi.com/api/v2/riskIntelligence`. As a premium service we offer an alternative endpoint that serves requests from datacenters in Germany only. You can specify `"eu"` or `"global"`, or a URL. ##### Example[​](#example-1 "Direct link to Example") ```
``` #### `data-start`[​](#data-start "Direct link to data-start") You can specify when the Risk Intelligence token should be generated using the `data-start` attribute. Here are the possible values: * `auto`: The token will be generated as soon as possible (i.e., once the element and SDK have loaded). * `focus` (**Default**): The token will be generated as soon as the element's parent `
` fires the `focusin` event. ##### Example[​](#example-2 "Direct link to Example") ```
``` #### `data-form-field-name`[​](#data-form-field-name "Direct link to data-form-field-name") By default, a hidden `` field is created in the element's parent `` with a `name="frc-risk-intelligence-token"` attribute. You can use `data-form-field-name` to change the `name` attribute of this hidden input. This can be useful for integrations with certain frameworks and content management systems. ##### Example[​](#example-3 "Direct link to Example") ```
``` ### Events[​](#events "Direct link to Events") If you create the Risk Intelligence token using an HTML element, the element will fire a number of JavaScript events that you can use to respond to changes in the token's lifecycle. The examples below assume the following markup (a `
` with an `id` of `"risk-intelligence-el"`). ```
``` #### `frc:riskintelligence.complete`[​](#frcriskintelligencecomplete "Direct link to frcriskintelligencecomplete") Fires when the token generation is complete and the token is available. ``` const el = document.getElementById("risk-intelligence-el"); el.addEventListener("frc:riskintelligence.complete", function (e) { console.info("Token generated", e.detail.token, e.detail.expiresAt); }); ``` For more information, see the SDK reference for [`frc:riskintelligence.complete`](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteevent.md). #### `frc:riskintelligence.error`[​](#frcriskintelligenceerror "Direct link to frcriskintelligenceerror") Fires if the token generation errors. ``` const el = document.getElementById("risk-intelligence-el"); el.addEventListener("frc:riskintelligence.error", function (e) { console.warn("Failed to generate token", e.detail.error); }); ``` For more information, see the SDK reference for [`frc:riskintelligence.error`](/docs/v2/sdk/reference/sdk.frcriskintelligenceerrorevent.md). #### `frc:riskintelligence.expire`[​](#frcriskintelligenceexpire "Direct link to frcriskintelligenceexpire") Fires when the token expires. ``` const el = document.getElementById("risk-intelligence-el"); el.addEventListener("frc:riskintelligence.expire", function () { console.info("Token expired."); }); ``` For more information, see the SDK reference for [`frc:riskintelligence.expire`](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireevent.md). *** ## Caching[​](#caching "Direct link to Caching") The SDK caches generated tokens to improve performance and efficiency. For example, if two `sdk.riskIntelligence()` calls get made (with the same sitekey and to the same API endpoint), they will receive the same token. Tokens are cached for approximately **15 minutes**. You can disable this behavior by setting the `bypassCache` configuration parameter. ``` const data = await sdk.riskIntelligence({ sitekey: "", bypassCache: true, }); ``` You can also clear currently cached Risk Intelligence tokens using the following SDK function. ``` await sdk.clearRiskIntelligence(); ``` You can configure the cache-clearing behavior of this function. See the SDK reference for [`sdk.clearRiskIntelligence()`](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clearriskintelligence.md). *** ## Using the token[​](#using-the-token "Direct link to Using the token") Once you have a token, you can send it in a `fetch` request or embed it in a `form` to be submitted to your server. ``` fetch("/api/action", { body: { token: data.token }, }); ``` With the token sent to your server, the next step is to use it to [retrieve Risk Intelligence data](/docs/v2/risk-intelligence/getting-started/retrieve.md). --- # Retrieve Risk Intelligence data To retrieve Risk Intelligence data using a token, you need to send a request containing the token authenticated by the Friendly Captcha API key you created in [the first step](/docs/v2/risk-intelligence/getting-started/setup.md#api-key). ### Send the retrieve request[​](#send-the-retrieve-request "Direct link to Send the retrieve request") To retrieve the data, send a **POST** request to `https://global.frcapi.com/api/v2/riskIntelligence/retrieve` with the following parameters. | POST Parameter | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------------------- | | `token` | `string` | The Risk Intelligence token generated in your application's front-end. | | `sitekey` | `string` | **Optional:** The sitekey used in toke generation, for an extra layer of back-end validation. | These parameters can be passed as JSON data or FormData. #### Authentication[​](#authentication "Direct link to Authentication") To authenticate the request, set the following header. | Header | Description | | ----------- | ----------------------------------------------------- | | `X-API-Key` | The API you created in your Friendly Captcha account. | ### The retrieve response[​](#the-retrieve-response "Direct link to The retrieve response") The response body is a JSON object with a `success` field that indicates whether the response is valid or not. #### Example `success=true` response[​](#example-successtrue-response "Direct link to example-successtrue-response") ``` { "success": true, "data": { "event_id": "ev_C52P4pm-xkZIHB5Y", // Unique identifier for the retrieve request. "token": { "timestamp": "2026-03-05T11:22:51.922Z", // ISO 8601 timestamp when the token was created. "expires_at": "2026-03-05T11:37:51.922Z", // ISO 8601 timestamp for when the token will expire. "num_uses": 1 // Counter representing the number of times the token has been used to retrieve Risk Intelligence data. }, "risk_intelligence": { // see https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/#risk-intelligence-data-format // for an example of Risk Intelligence data. } } } ``` #### Example `success=false` response[​](#example-successfalse-response "Direct link to example-successfalse-response") ``` { "success": false, "error": { "error_code": "bad_request", // Error code, see the table below for possible values "detail": "..." // Extra details (this is mainly intended for Friendly Captcha staff) }, } ``` info We aim to ***never*** fundamentally change or remove *existing* fields from these responses. However, you should expect us to add *new* fields in the future. Below is a list of possible values for `error_code`. **If you are seeing status code 400 or 401 your server code is probably not configured correctly.** | Error code | Status | Description | | ----------------- | ------ | ---------------------------------------------------------------------- | | `auth_required` | 401 | You forgot to set the `X-API-Key` header. | | `auth_invalid` | 401 | The API key you provided was invalid. | | `sitekey_invalid` | 400 | The sitekey in your request is invalid. | | `token_missing` | 400 | The token parameter is missing or empty. | | `bad_request` | 400 | Something else is wrong with your request; e.g. your token is invalid. | | `token_invalid` | 200 | The token is invalid (e.g., associated with a different account). | | `token_expired` | 200 | The token is expired. | Note Status code **200** does not necessarily mean that the token was successfully used to retrieve Risk Intelligence data; it just means that the API was able to handle it. Check the `success` field. --- # Create a sitekey and API key ## Sitekey[​](#sitekey "Direct link to Sitekey") Log in to your Friendly Captcha account and head to the [Applications](https://app.friendlycaptcha.eu/dashboard/accounts/-/apps) page. Click **+ New Application** and enter the necessary details. After you've created the application, take note of the **sitekey** value under the application name—we'll need it in the following steps. Did you know? A sitekey always starts with the characters `FC`. ## API Key[​](#api-key "Direct link to API Key") You can create an API key on the [**API Keys**](https://app.friendlycaptcha.eu/dashboard/accounts/-/keys) page in the dashboard. Note Your new API key will only be displayed once, for security reasons, so make sure to save it somewhere safe. Once you have your sitekey and your API key, you are ready to[ generate a Risk Intelligence token](/docs/v2/risk-intelligence/getting-started/generate.md). --- # Risk Intelligence On Captcha Challenge As a paid add-on we offer Risk Intelligence data when verifying any Friendly Captcha v2 challenge. This feature simplifies your integration by allowing you to get risk scores and signals about the user who solved the captcha challenge without needing to make a separate API call to our Risk Intelligence API or change your frontend integration. With Risk Intelligence on Captcha Challenge you get risk insights about the user at the moment they have solved the captcha, which can help you make decisions about how to handle the request. For example, if a user has a high-risk score, you might want to require additional verification steps, despite them solving the captcha challenge. Alternatively, you can store this data for auditing and monitoring purposes, or feed it into your existing fraud detection systems. This allows you to enhance your security and fraud detection capabilities without adding any friction to the user experience. When enabled, the Risk Intelligence data will be included in the `risk_intelligence` field of the response from the [siteverify API](/docs/v2/api/siteverify.md). The data in the field is described in detail on the [Risk Intelligence Format](/docs/v2/risk-intelligence/format.md) page. --- # Use Cases Risk Intelligence data can be used in various ways to enhance the security and fraud detection capabilities of your applications. Here we describe some common use cases for Risk Intelligence data. ## Risk-based Authentication[​](#risk-based-authentication "Direct link to Risk-based Authentication") Risk Intelligence can be used to implement **risk-based authentication (RBA)** for preventing *account takeover*. With RBA you adjust the authentication requirements based on the assessed risk of the login attempt. For example, if a user is logging in from a new device or location and has a high-risk score, you might require them to complete additional verification steps such as entering a one-time password sent to their email or phone. A Risk-Based Authentication setup helps prevent account takeover attacks while allowing low-risk users to log in without additional friction. Even if the account credentials have been compromised, the additional verification steps can block unauthorized access. In practice this means an end-user may need to click a link in their email inbox or enter a code sent via SMS to complete the login process. *"You are logging in from a new device, please verify your identity by entering the code sent to your email."* Example: Risk-Based Authentication Email ![Example of a Risk-Based Authentication email](/mockups/risk-auth-email.png) *A security alert email notifying the user of an unusual login attempt from a new location, with options to confirm or secure their account.* ## Fraud Detection and Prevention[​](#fraud-detection-and-prevention "Direct link to Fraud Detection and Prevention") Risk Intelligence data can be integrated into your existing fraud detection systems to help identify and block fraudulent activity. For example, if a user is attempting to create multiple accounts from a country that you don't usually see traffic from, and has a high-risk score, you might want to block the account creation or flag it for manual review. Example: Fraud Detection Dashboard ![Fraud Detection Dashboard showing flagged signups with risk scores](/mockups/fraud-detection-dashboard.png) *A fraud detection dashboard showing high-risk signups awaiting review with detailed risk signals including VPN detection, AS type, network risk scores, and device information.* ## Auditing and Monitoring[​](#auditing-and-monitoring "Direct link to Auditing and Monitoring") You can use Risk Intelligence data for auditing and monitoring purposes. By logging the risk scores and signals associated with user interactions, you can analyze patterns of behavior and identify potential security threats. For example, you might notice a spike in high-risk scores from a particular network, geographic region or device type, which could indicate a coordinated attack. You can feed this data into your SIEM (Security Information and Event Management) system to correlate it with other security events. ## Account Sharing Prevention[​](#account-sharing-prevention "Direct link to Account Sharing Prevention") Risk Intelligence can also help you detect and prevent account sharing. If you see a user logging in from multiple different browsers, devices or locations, it could indicate that the account is being shared among multiple people, which may violate your terms of service. Example: Account Sharing Detection ![Account Sharing Detection showing concurrent sessions from multiple locations](/mockups/account-sharing-detection.png) *An account sharing detection view showing concurrent active sessions from New York, London, and Sydney with detailed device information and geographic distribution on a world map.* ## Known Bot Identification[​](#known-bot-identification "Direct link to Known Bot Identification") More and more bots are active on the internet with the rise in LLMs and generative AI. > *"ChatGPT go to this website, fill in the job application form with my details and submit it"*. Risk Intelligence helps you identify these known bots,automated traffic, and user-initiated AI automation, allowing you to decide what to do with this traffic. Example: Bot Detection Dashboard ![Bot Detection Dashboard showing bot traffic analysis](/mockups/bot-detection-dashboard.png) *A bot detection dashboard showing traffic distribution between humans and bots, top detected bots (GPTBot, Scrapy, ClaudeBot), and detailed bot activity with risk scores.* ## Custom Use Cases[​](#custom-use-cases "Direct link to Custom Use Cases") The modular nature of Risk Intelligence allows you to use the specific data points that are most relevant to your use case. For example, if you are particularly concerned about users using anonymization services such as VPNs or proxies, you can focus on the signals provided by the **Anonymization Detection** module. --- # Widget SDK The SDK is the code you add to your website to display captcha widgets on your website. The SDK is open source [**here**](https://github.com/friendlycaptcha/friendly-captcha-sdk). ## SDK documentation[​](#sdk-documentation "Direct link to SDK documentation") ### [Widget Configuration](/docs/v2/sdk/configuration.md)[​](#widget-configuration "Direct link to widget-configuration") Options when creating a widget. ### [Widget Events](/docs/v2/sdk/events.md)[​](#widget-events "Direct link to widget-events") Events your Javascript code can listen to to react to changes in the widget. This is often used to enable or disable the submit button of your web forms. ### [Widget Lifecycle](/docs/v2/sdk/lifecycle.md)[​](#widget-lifecycle "Direct link to widget-lifecycle") The widget has a fixed amount of states it can be in. ### [SDK Reference](/docs/v2/sdk/reference/sdk.md)[​](#sdk-reference "Direct link to sdk-reference") The code documentation generated from the [SDK codebase](https://github.com/friendlycaptcha/friendly-captcha-sdk). --- # Configuration There are two ways you can configure the behavior of the widget. * You can use **HTML attributes** (such as `data-start`). This is the method you usually use if you installed the widget to your website by adding a ` ``` ## Widget Event Reference[​](#widget-event-reference "Direct link to Widget Event Reference") Below are all the custom events a widget can emit and their payload. ### `frc:widget.complete`[​](#frcwidgetcomplete "Direct link to frcwidgetcomplete") Called when the widget completes, in the payload you can find the `response` which is the value that should be part of the request to your server. #### Event payload (**`event.detail`**)[​](#event-payload-eventdetail "Direct link to event-payload-eventdetail") ``` { name: "frc:widget.complete", state: "completed", response: "", id: "w_123asdf" // The widget ID, a random string that is unique per widget on the page. } ``` ### `frc:widget.error`[​](#frcwidgeterror "Direct link to frcwidgeterror") Called when the widget failed for any reason. Perhaps there was no internet connection to request a challenge. A restart button is shown to the end user and the widget can be started to try again. #### **`event.detail`**[​](#eventdetail "Direct link to eventdetail") ``` { name: "frc:widget.error", state: "error", response: ".ERROR", error: { code: "network_error", // See `WidgetErrorCode` for possible values detail: "..." // A human readable description of the issue, or an empty string. }, id: "w_123asdf" // The widget ID, a random string that is unique per widget on the page. } ``` ### `frc:widget.expire`[​](#frcwidgetexpire "Direct link to frcwidgetexpire") Called when the completed response has expired. This will only happen if the user leaves their browser open for a long time after completing a captcha, in which case a restart button is shown to the end user. #### **`event.detail`**[​](#eventdetail-1 "Direct link to eventdetail-1") ``` { name: "frc:widget.expire", state: "expired", response: ".EXPIRED", id: "w_123asdf" // The widget ID, a random string that is unique per widget on the page. } ``` ### `frc:widget.statechange`[​](#frcwidgetstatechange "Direct link to frcwidgetstatechange") Called when the widget switches to a different state.
See also the [**widget lifecycle documentation**](/docs/v2/sdk/lifecycle.md). #### **`event.detail`**[​](#eventdetail-2 "Direct link to eventdetail-2") ``` { name: "frc:widget.statechange", state: "", // One of "init", "reset", "unactivated", "activating", "activated", "requesting", "solving", "verifying", "completed", "expired", "error", "destroyed" response: "", error: { code: "network_error", // See `WidgetErrorCode` for possible values detail: "..." // A human description of the issue, or empty string. }, id: "w_123asdf" // The widget ID, a random string that is unique per widget on the page. } ``` ### WidgetError[​](#widgeterror "Direct link to WidgetError") For the exact specification of the `error` fields in the event payload, see the [reference here](/docs/v2/sdk/reference/sdk.widgeterrordata.md). --- # Lifecycle The widget has a fixed set of states it can be in. Usually the only states you have to care about are the following: * `completed`: the captcha has been completed, you can enable the button to submit and verification should pass. * `error`: something went wrong. Enable the button to submit so that the error can be investigated. * `expired`: the user waited too long and needs to restart. You should disable the submit button. * `destroyed`: You called `destroy()` on the widget which cleans it up entirely, it can no longer be used. Below is a diagram of the entire flow. ## Lifecycle Diagram[​](#lifecycle-diagram "Direct link to Lifecycle Diagram") ![Widget Lifecycle Diagram](/assets/images/widget_lifecycle-31b5b7ee121841300de03b076700eb28.png) --- [Home](/docs/v2/sdk/reference/.md) ## API Reference[​](#api-reference "Direct link to API Reference") ## Packages[​](#packages "Direct link to Packages") | Package | Description | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) | A library for integrating Friendly Captcha into your website. This SDK allows you to create captcha widgets, respond to their changes, and interact with them programmatically. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) ## sdk package[​](#sdk-package "Direct link to sdk package") A library for integrating Friendly Captcha into your website. This SDK allows you to create captcha widgets, respond to their changes, and interact with them programmatically. ## Classes[​](#classes "Direct link to Classes") | Class | Description | | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) | Main entry point for V2 of the Friendly Captcha SDK. This class keeps track of widgets and allows you to create widgets programmatically.Generally there should only be one instance of this SDK in your website. | | [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) | This provides a handle for configuring and managing a Risk Intelligence request via an HTML element.This class is only instantiated by the SDK - do not create a handle yourself. | | [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) | This provides an API stub that provides the end-user JS API for a widget.This class is only instantiated by the SDK - do not create a handle yourself. | ## Interfaces[​](#interfaces "Direct link to Interfaces") | Interface | Description | | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) | Options when creating a widget programmatically. | | [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) | A DOM event map for all events that can be dispatched by a widget. | | [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) | Payload of the `"frc:riskintelligence.complete"` event. | | [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md) | Payload of the `"frc:riskintelligence.error"` event. | | [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md) | Payload of the `"frc:riskintelligence.expire"` event. | | [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) | Payload of the `"frc:widget.complete"` event. | | [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) | Payload of the `"frc:widget.error"` event. | | [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) | Payload of the `"frc:widget.expire"` event. | | [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) | Payload of the `"frc:widget.reset"` event. | | [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) | Payload of the `"frc:widget.statechange"` event. | | [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) | Options when creating a new SDK instance. | | [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md) | Options for clearing cached Risk Intelligence tokens. | | [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md) | Error data returned by a failed Risk Intelligence request. | | [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md) | Data returned by the Risk Intelligence API request. | | [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) | Options for configuring a Risk Intelligence request. | | [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) | | | [WidgetResetOptions](/docs/v2/sdk/reference/sdk.widgetresetoptions.md) | The options object you can pass to the `widget.reset()` method. | ## Variables[​](#variables "Direct link to Variables") | Variable | Description | | ---------------------------------------------------------------------------------------------------------- | --------------------------------- | | [FRCRiskIntelligenceCompleteEventName](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventname.md) | `"frc:riskintelligence.complete"` | | [FRCRiskIntelligenceErrorEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventname.md) | `"frc:riskintelligence.error"` | | [FRCRiskIntelligenceExpireEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventname.md) | `"frc:riskintelligence.expire"` | | [FRCWidgetCompleteEventName](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventname.md) | `"frc:widget.complete"` | | [FRCWidgetErrorEventName](/docs/v2/sdk/reference/sdk.frcwidgeterroreventname.md) | `"frc:widget.error"` | | [FRCWidgetExpireEventName](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventname.md) | `"frc:widget.expire"` | | [FRCWidgetResetEventName](/docs/v2/sdk/reference/sdk.frcwidgetreseteventname.md) | `"frc:widget.reset"` | | [FRCWidgetStateChangeEventName](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventname.md) | `"frc:widget.statechange"` | ## Type Aliases[​](#type-aliases "Direct link to Type Aliases") | Type Alias | Description | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) | Which API endpoint to use for the SDK. Typically "eu" or "global", but a URL can be used to specify a custom endpoint. Defaults to "global". | | [FRCEventData](/docs/v2/sdk/reference/sdk.frceventdata.md) | Payloads of any of the events that can be dispatched by a widget. | | [FRCEventName](/docs/v2/sdk/reference/sdk.frceventname.md) | Names of any of the events that can be dispatched by a widget. | | [FRCRiskIntelligenceCompleteEvent](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteevent.md) | Event that gets dispatched when a Risk Intelligence token has been successfully generated. | | [FRCRiskIntelligenceErrorEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceerrorevent.md) | Event that gets dispatched when the Risk Intelligence request fails. | | [FRCRiskIntelligenceExpireEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireevent.md) | Event that gets dispatched when a Risk Intelligence token expires. | | [FRCWidgetCompleteEvent](/docs/v2/sdk/reference/sdk.frcwidgetcompleteevent.md) | Event that gets dispatched when the widget is completed. This happens when the user's browser has succesfully passed the captcha challenge. | | [FRCWidgetStateChangeEvent](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeevent.md) | Event that gets dispatched when the widget enters a new state. | | [FRCWidgetWidgetErrorEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgeterrorevent.md) | Event that gets dispatched when something goes wrong in the widget. | | [FRCWidgetWidgetExpireEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetexpireevent.md) | Event that gets dispatched when the widget expires. This happens when the user takes too long to submit the captcha after it is solved. | | [FRCWidgetWidgetResetEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetresetevent.md) | Event that gets dispatched when something goes wrong in the widget. | | [RiskIntelligenceErrorCode](/docs/v2/sdk/reference/sdk.riskintelligenceerrorcode.md) | Error codes that can be returned by the Risk Intelligence request.See [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) for the list of possible error codes. | | [SentinelResponse](/docs/v2/sdk/reference/sdk.sentinelresponse.md) | Response values used in the hidden input field when no valid solution is present, these tell you something about the state of the widget. | | [StartMode](/docs/v2/sdk/reference/sdk.startmode.md) | The start mode of the widget.\* `"auto"`: the widget gets activated as soon as it is created. \* `"focus"`: the widget gets activated as soon as the form above it is focused. \* `"none"`: The widget does not get activated automatically at all, the user needs to press the widget (or `.start()` gets called using the Javascript API). | | [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) | Error codes that can be returned by the widget.\* `"network_error"`: The user's browser could not connect to the Friendly Captcha API. \* `"sitekey_invalid"`: The sitekey is invalid. \* `"sitekey_missing"`: The sitekey is missing. \* `"other"`: Some other error occurred.In all cases it's the best practice to enable the "submit" button when the widget errors, so that the user can still perform the action despite not having solved the captcha. | | [WidgetMode](/docs/v2/sdk/reference/sdk.widgetmode.md) | The mode of the widget. \* `"interactive"` (default): the widget is interactive: the user needs to click the checkbox to finalize the captcha. This mode offers the best anti-bot protection. \* `"noninteractive"`: the widget is non-interactive: the captcha is solved without any user interaction required. | | [WidgetResetTrigger](/docs/v2/sdk/reference/sdk.widgetresettrigger.md) | What caused the widget to reset. \* `root`: Code on the root page (= your website code or plugin) caused the reset. \* `widget`: The reset came from the widget. The user likely clicked the reset button within the widget. \* `agent`: The reset came from the agent - this currently does not happen but may in the future. | | [WidgetState](/docs/v2/sdk/reference/sdk.widgetstate.md) | The state the widget is in. See the [widget lifecycle](/docs/v2/sdk/lifecycle.md) docs for more information. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) ## APIEndpoint type[​](#apiendpoint-type "Direct link to APIEndpoint type") Which API endpoint to use for the SDK. Typically "eu" or "global", but a URL can be used to specify a custom endpoint. Defaults to "global". **Signature:** ``` export type APIEndpoint = string | "eu" | "global"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) ## CreateWidgetOptions interface[​](#createwidgetoptions-interface "Direct link to CreateWidgetOptions interface") Options when creating a widget programmatically. **Signature:** ``` export interface CreateWidgetOptions ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [apiEndpoint?](/docs/v2/sdk/reference/sdk.createwidgetoptions.apiendpoint.md) | | [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) | *(Optional)* A custom endpoint from which the agent and widgets are loaded. | | [element](/docs/v2/sdk/reference/sdk.createwidgetoptions.element.md) | | HTMLElement | The HTML element to mount to, usually this is an element with class `.frc-captcha`. | | [formFieldName?](/docs/v2/sdk/reference/sdk.createwidgetoptions.formfieldname.md) | | string \| null | *(Optional)* The name of the field in the form that is set, defaults to `frc-captcha-response`. | | [language?](/docs/v2/sdk/reference/sdk.createwidgetoptions.language.md) | | string | *(Optional)* Language code such as "en" for English or "de" for German. Defaults to automatic language detection.Usually you should not set this yourself and instead let the widget detect the language automatically. | | [sitekey?](/docs/v2/sdk/reference/sdk.createwidgetoptions.sitekey.md) | | string | *(Optional)* Sitekey of your application, starts with `FC`. | | [startMode?](/docs/v2/sdk/reference/sdk.createwidgetoptions.startmode.md) | | [StartMode](/docs/v2/sdk/reference/sdk.startmode.md) | *(Optional)* The start mode determines the behavior around automatic activation of the widget. Activation here means the challenge gets requested and gets solved. Defaults to `"focus"`.\* `"auto"`: the widget gets activated as soon as it is created. \* `"focus"`: the widget gets activated as soon as the form above it is focused. \* `"none"`: The widget does not start automatically at all, the user needs to press the widget. | | [theme?](/docs/v2/sdk/reference/sdk.createwidgetoptions.theme.md) | | "light" \| "dark" \| "auto" | *(Optional)* The theme for the widget.\* `"light"` (default): a light theme with a white background. \* `"dark"`: a dark theme with a dark background. \* `"auto"`: the theme is automatically chosen based on the user's system preferences. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [apiEndpoint](/docs/v2/sdk/reference/sdk.createwidgetoptions.apiendpoint.md) ## CreateWidgetOptions.apiEndpoint property[​](#createwidgetoptionsapiendpoint-property "Direct link to CreateWidgetOptions.apiEndpoint property") A custom endpoint from which the agent and widgets are loaded. **Signature:** ``` apiEndpoint?: APIEndpoint; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [element](/docs/v2/sdk/reference/sdk.createwidgetoptions.element.md) ## CreateWidgetOptions.element property[​](#createwidgetoptionselement-property "Direct link to CreateWidgetOptions.element property") The HTML element to mount to, usually this is an element with class `.frc-captcha`. **Signature:** ``` element: HTMLElement; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [formFieldName](/docs/v2/sdk/reference/sdk.createwidgetoptions.formfieldname.md) ## CreateWidgetOptions.formFieldName property[​](#createwidgetoptionsformfieldname-property "Direct link to CreateWidgetOptions.formFieldName property") The name of the field in the form that is set, defaults to `frc-captcha-response`. **Signature:** ``` formFieldName?: string | null; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [language](/docs/v2/sdk/reference/sdk.createwidgetoptions.language.md) ## CreateWidgetOptions.language property[​](#createwidgetoptionslanguage-property "Direct link to CreateWidgetOptions.language property") Language code such as "en" for English or "de" for German. Defaults to automatic language detection. Usually you should not set this yourself and instead let the widget detect the language automatically. **Signature:** ``` language?: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [sitekey](/docs/v2/sdk/reference/sdk.createwidgetoptions.sitekey.md) ## CreateWidgetOptions.sitekey property[​](#createwidgetoptionssitekey-property "Direct link to CreateWidgetOptions.sitekey property") Sitekey of your application, starts with `FC`. **Signature:** ``` sitekey?: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [startMode](/docs/v2/sdk/reference/sdk.createwidgetoptions.startmode.md) ## CreateWidgetOptions.startMode property[​](#createwidgetoptionsstartmode-property "Direct link to CreateWidgetOptions.startMode property") The start mode determines the behavior around automatic activation of the widget. Activation here means the challenge gets requested and gets solved. Defaults to `"focus"`. \* `"auto"`: the widget gets activated as soon as it is created. \* `"focus"`: the widget gets activated as soon as the form above it is focused. \* `"none"`: The widget does not start automatically at all, the user needs to press the widget. **Signature:** ``` startMode?: StartMode; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) > [theme](/docs/v2/sdk/reference/sdk.createwidgetoptions.theme.md) ## CreateWidgetOptions.theme property[​](#createwidgetoptionstheme-property "Direct link to CreateWidgetOptions.theme property") The theme for the widget. \* `"light"` (default): a light theme with a white background. \* `"dark"`: a dark theme with a dark background. \* `"auto"`: the theme is automatically chosen based on the user's system preferences. **Signature:** ``` theme?: "light" | "dark" | "auto"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventData](/docs/v2/sdk/reference/sdk.frceventdata.md) ## FRCEventData type[​](#frceventdata-type "Direct link to FRCEventData type") Payloads of any of the events that can be dispatched by a widget. **Signature:** ``` export type FRCEventData = FRCWidgetStateChangeEventData | FRCWidgetCompleteEventData | FRCWidgetExpireEventData | FRCWidgetErrorEventData | FRCWidgetResetEventData | FRCRiskIntelligenceCompleteEventData | FRCRiskIntelligenceErrorEventData | FRCRiskIntelligenceExpireEventData; ``` **References:** [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md), [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md), [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md), [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md), [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md), [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md), [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md), [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) ## FRCEventMap interface[​](#frceventmap-interface "Direct link to FRCEventMap interface") A DOM event map for all events that can be dispatched by a widget. **Signature:** ``` export interface FRCEventMap ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ---------------------------------------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------- | ----------- | | ["frc:riskintelligence.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.complete_.md) | | [FRCRiskIntelligenceCompleteEvent](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteevent.md) | | | ["frc:riskintelligence.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.error_.md) | | [FRCRiskIntelligenceErrorEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceerrorevent.md) | | | ["frc:riskintelligence.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.expire_.md) | | [FRCRiskIntelligenceExpireEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireevent.md) | | | ["frc:widget.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.complete_.md) | | [FRCWidgetCompleteEvent](/docs/v2/sdk/reference/sdk.frcwidgetcompleteevent.md) | | | ["frc:widget.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.error_.md) | | [FRCWidgetWidgetErrorEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgeterrorevent.md) | | | ["frc:widget.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.expire_.md) | | [FRCWidgetWidgetExpireEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetexpireevent.md) | | | ["frc:widget.reset"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.reset_.md) | | [FRCWidgetWidgetResetEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetresetevent.md) | | | ["frc:widget.statechange"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.statechange_.md) | | [FRCWidgetStateChangeEvent](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeevent.md) | | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:riskintelligence.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.complete_.md) ## FRCEventMap."frc:riskintelligence.complete" property[​](#frceventmapfrccomplete-property "Direct link to frceventmapfrccomplete-property") **Signature:** ``` [FRCRiskIntelligenceCompleteEventName]: FRCRiskIntelligenceCompleteEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:riskintelligence.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.error_.md) ## FRCEventMap."frc:riskintelligence.error" property[​](#frceventmapfrcerror-property "Direct link to frceventmapfrcerror-property") **Signature:** ``` [FRCRiskIntelligenceErrorEventName]: FRCRiskIntelligenceErrorEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:riskintelligence.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_riskintelligence.expire_.md) ## FRCEventMap."frc:riskintelligence.expire" property[​](#frceventmapfrcexpire-property "Direct link to frceventmapfrcexpire-property") **Signature:** ``` [FRCRiskIntelligenceExpireEventName]: FRCRiskIntelligenceExpireEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:widget.complete"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.complete_.md) ## FRCEventMap."frc:widget.complete" property[​](#frceventmapfrccomplete-property "Direct link to frceventmapfrccomplete-property") **Signature:** ``` [FRCWidgetCompleteEventName]: FRCWidgetCompleteEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:widget.error"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.error_.md) ## FRCEventMap."frc:widget.error" property[​](#frceventmapfrcerror-property "Direct link to frceventmapfrcerror-property") **Signature:** ``` [FRCWidgetErrorEventName]: FRCWidgetWidgetErrorEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:widget.expire"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.expire_.md) ## FRCEventMap."frc:widget.expire" property[​](#frceventmapfrcexpire-property "Direct link to frceventmapfrcexpire-property") **Signature:** ``` [FRCWidgetExpireEventName]: FRCWidgetWidgetExpireEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:widget.reset"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.reset_.md) ## FRCEventMap."frc:widget.reset" property[​](#frceventmapfrcreset-property "Direct link to frceventmapfrcreset-property") **Signature:** ``` [FRCWidgetResetEventName]: FRCWidgetWidgetResetEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) > ["frc:widget.statechange"](/docs/v2/sdk/reference/sdk.frceventmap._frc_widget.statechange_.md) ## FRCEventMap."frc:widget.statechange" property[​](#frceventmapfrcstatechange-property "Direct link to frceventmapfrcstatechange-property") **Signature:** ``` [FRCWidgetStateChangeEventName]: FRCWidgetStateChangeEvent; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCEventName](/docs/v2/sdk/reference/sdk.frceventname.md) ## FRCEventName type[​](#frceventname-type "Direct link to FRCEventName type") Names of any of the events that can be dispatched by a widget. **Signature:** ``` export type FRCEventName = keyof FRCEventMap; ``` **References:** [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEvent](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteevent.md) ## FRCRiskIntelligenceCompleteEvent type[​](#frcriskintelligencecompleteevent-type "Direct link to FRCRiskIntelligenceCompleteEvent type") Event that gets dispatched when a Risk Intelligence token has been successfully generated. **Signature:** ``` export type FRCRiskIntelligenceCompleteEvent = CustomEvent; ``` **References:** [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) ## FRCRiskIntelligenceCompleteEventData interface[​](#frcriskintelligencecompleteeventdata-interface "Direct link to FRCRiskIntelligenceCompleteEventData interface") Payload of the `"frc:riskintelligence.complete"` event. **Signature:** ``` export interface FRCRiskIntelligenceCompleteEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ----------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | [expiresAt](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.expiresat.md) | | number | A timestamp, represented as a Unix epoch, for when the Risk Intelligence token will expire. | | [name](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.name.md) | | typeof [FRCRiskIntelligenceCompleteEventName](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventname.md) | `"frc:riskintelligence.complete"` | | [token](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.token.md) | | string | The Risk Intelligence response token, to be verified server-side. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) > [expiresAt](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.expiresat.md) ## FRCRiskIntelligenceCompleteEventData.expiresAt property[​](#frcriskintelligencecompleteeventdataexpiresat-property "Direct link to FRCRiskIntelligenceCompleteEventData.expiresAt property") A timestamp, represented as a Unix epoch, for when the Risk Intelligence token will expire. **Signature:** ``` expiresAt: number; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.name.md) ## FRCRiskIntelligenceCompleteEventData.name property[​](#frcriskintelligencecompleteeventdataname-property "Direct link to FRCRiskIntelligenceCompleteEventData.name property") `"frc:riskintelligence.complete"` **Signature:** ``` name: typeof FRCRiskIntelligenceCompleteEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEventData](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.md) > [token](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventdata.token.md) ## FRCRiskIntelligenceCompleteEventData.token property[​](#frcriskintelligencecompleteeventdatatoken-property "Direct link to FRCRiskIntelligenceCompleteEventData.token property") The Risk Intelligence response token, to be verified server-side. **Signature:** ``` token: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceCompleteEventName](/docs/v2/sdk/reference/sdk.frcriskintelligencecompleteeventname.md) ## FRCRiskIntelligenceCompleteEventName variable[​](#frcriskintelligencecompleteeventname-variable "Direct link to FRCRiskIntelligenceCompleteEventName variable") `"frc:riskintelligence.complete"` **Signature:** ``` FRCRiskIntelligenceCompleteEventName = "frc:riskintelligence.complete" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceErrorEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceerrorevent.md) ## FRCRiskIntelligenceErrorEvent type[​](#frcriskintelligenceerrorevent-type "Direct link to FRCRiskIntelligenceErrorEvent type") Event that gets dispatched when the Risk Intelligence request fails. **Signature:** ``` export type FRCRiskIntelligenceErrorEvent = CustomEvent; ``` **References:** [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md) ## FRCRiskIntelligenceErrorEventData interface[​](#frcriskintelligenceerroreventdata-interface "Direct link to FRCRiskIntelligenceErrorEventData interface") Payload of the `"frc:riskintelligence.error"` event. **Signature:** ``` export interface FRCRiskIntelligenceErrorEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ------------------------------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------ | | [error](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.error.md) | | [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md) | The error that occurred. | | [name](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.name.md) | | typeof [FRCRiskIntelligenceErrorEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventname.md) | `"frc:riskintelligence.error"` | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md) > [error](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.error.md) ## FRCRiskIntelligenceErrorEventData.error property[​](#frcriskintelligenceerroreventdataerror-property "Direct link to FRCRiskIntelligenceErrorEventData.error property") The error that occurred. **Signature:** ``` error: RiskIntelligenceErrorData; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceErrorEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventdata.name.md) ## FRCRiskIntelligenceErrorEventData.name property[​](#frcriskintelligenceerroreventdataname-property "Direct link to FRCRiskIntelligenceErrorEventData.name property") `"frc:riskintelligence.error"` **Signature:** ``` name: typeof FRCRiskIntelligenceErrorEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceErrorEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceerroreventname.md) ## FRCRiskIntelligenceErrorEventName variable[​](#frcriskintelligenceerroreventname-variable "Direct link to FRCRiskIntelligenceErrorEventName variable") `"frc:riskintelligence.error"` **Signature:** ``` FRCRiskIntelligenceErrorEventName = "frc:riskintelligence.error" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceExpireEvent](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireevent.md) ## FRCRiskIntelligenceExpireEvent type[​](#frcriskintelligenceexpireevent-type "Direct link to FRCRiskIntelligenceExpireEvent type") Event that gets dispatched when a Risk Intelligence token expires. **Signature:** ``` export type FRCRiskIntelligenceExpireEvent = CustomEvent; ``` **References:** [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md) ## FRCRiskIntelligenceExpireEventData interface[​](#frcriskintelligenceexpireeventdata-interface "Direct link to FRCRiskIntelligenceExpireEventData interface") Payload of the `"frc:riskintelligence.expire"` event. **Signature:** ``` export interface FRCRiskIntelligenceExpireEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ----------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------- | | [name](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.name.md) | | typeof [FRCRiskIntelligenceExpireEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventname.md) | `"frc:riskintelligence.expire"` | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceExpireEventData](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventdata.name.md) ## FRCRiskIntelligenceExpireEventData.name property[​](#frcriskintelligenceexpireeventdataname-property "Direct link to FRCRiskIntelligenceExpireEventData.name property") `"frc:riskintelligence.expire"` **Signature:** ``` name: typeof FRCRiskIntelligenceExpireEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCRiskIntelligenceExpireEventName](/docs/v2/sdk/reference/sdk.frcriskintelligenceexpireeventname.md) ## FRCRiskIntelligenceExpireEventName variable[​](#frcriskintelligenceexpireeventname-variable "Direct link to FRCRiskIntelligenceExpireEventName variable") `"frc:riskintelligence.expire"` **Signature:** ``` FRCRiskIntelligenceExpireEventName = "frc:riskintelligence.expire" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEvent](/docs/v2/sdk/reference/sdk.frcwidgetcompleteevent.md) ## FRCWidgetCompleteEvent type[​](#frcwidgetcompleteevent-type "Direct link to FRCWidgetCompleteEvent type") Event that gets dispatched when the widget is completed. This happens when the user's browser has succesfully passed the captcha challenge. **Signature:** ``` export type FRCWidgetCompleteEvent = CustomEvent; ``` **References:** [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) ## FRCWidgetCompleteEventData interface[​](#frcwidgetcompleteeventdata-interface "Direct link to FRCWidgetCompleteEventData interface") Payload of the `"frc:widget.complete"` event. **Signature:** ``` export interface FRCWidgetCompleteEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ----------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------- | --------------------------------------------- | | [id](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.id.md) | | string | The widget ID that the event originated from. | | [name](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.name.md) | | typeof [FRCWidgetCompleteEventName](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventname.md) | `"frc:widget.complete"` | | [response](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.response.md) | | string | The current `frc-captcha-response` value. | | [state](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.state.md) | | "completed" | | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) > [id](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.id.md) ## FRCWidgetCompleteEventData.id property[​](#frcwidgetcompleteeventdataid-property "Direct link to FRCWidgetCompleteEventData.id property") The widget ID that the event originated from. **Signature:** ``` id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.name.md) ## FRCWidgetCompleteEventData.name property[​](#frcwidgetcompleteeventdataname-property "Direct link to FRCWidgetCompleteEventData.name property") `"frc:widget.complete"` **Signature:** ``` name: typeof FRCWidgetCompleteEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) > [response](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.response.md) ## FRCWidgetCompleteEventData.response property[​](#frcwidgetcompleteeventdataresponse-property "Direct link to FRCWidgetCompleteEventData.response property") The current `frc-captcha-response` value. **Signature:** ``` response: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventData](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.md) > [state](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventdata.state.md) ## FRCWidgetCompleteEventData.state property[​](#frcwidgetcompleteeventdatastate-property "Direct link to FRCWidgetCompleteEventData.state property") **Signature:** ``` state: "completed"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetCompleteEventName](/docs/v2/sdk/reference/sdk.frcwidgetcompleteeventname.md) ## FRCWidgetCompleteEventName variable[​](#frcwidgetcompleteeventname-variable "Direct link to FRCWidgetCompleteEventName variable") `"frc:widget.complete"` **Signature:** ``` FRCWidgetCompleteEventName = "frc:widget.complete" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) ## FRCWidgetErrorEventData interface[​](#frcwidgeterroreventdata-interface "Direct link to FRCWidgetErrorEventData interface") Payload of the `"frc:widget.error"` event. **Signature:** ``` export interface FRCWidgetErrorEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | -------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------- | --------------------------------------------- | | [error](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.error.md) | | [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) | The error that caused the state change. | | [id](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.id.md) | | string | The widget ID that the event originated from. | | [name](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.name.md) | | typeof [FRCWidgetErrorEventName](/docs/v2/sdk/reference/sdk.frcwidgeterroreventname.md) | `"frc:widget.error"` | | [response](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.response.md) | | string | The current `frc-captcha-response` value. | | [state](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.state.md) | | "error" | | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) > [error](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.error.md) ## FRCWidgetErrorEventData.error property[​](#frcwidgeterroreventdataerror-property "Direct link to FRCWidgetErrorEventData.error property") The error that caused the state change. **Signature:** ``` error: WidgetErrorData; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) > [id](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.id.md) ## FRCWidgetErrorEventData.id property[​](#frcwidgeterroreventdataid-property "Direct link to FRCWidgetErrorEventData.id property") The widget ID that the event originated from. **Signature:** ``` id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.name.md) ## FRCWidgetErrorEventData.name property[​](#frcwidgeterroreventdataname-property "Direct link to FRCWidgetErrorEventData.name property") `"frc:widget.error"` **Signature:** ``` name: typeof FRCWidgetErrorEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) > [response](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.response.md) ## FRCWidgetErrorEventData.response property[​](#frcwidgeterroreventdataresponse-property "Direct link to FRCWidgetErrorEventData.response property") The current `frc-captcha-response` value. **Signature:** ``` response: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) > [state](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.state.md) ## FRCWidgetErrorEventData.state property[​](#frcwidgeterroreventdatastate-property "Direct link to FRCWidgetErrorEventData.state property") **Signature:** ``` state: "error"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetErrorEventName](/docs/v2/sdk/reference/sdk.frcwidgeterroreventname.md) ## FRCWidgetErrorEventName variable[​](#frcwidgeterroreventname-variable "Direct link to FRCWidgetErrorEventName variable") `"frc:widget.error"` **Signature:** ``` FRCWidgetErrorEventName = "frc:widget.error" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) ## FRCWidgetExpireEventData interface[​](#frcwidgetexpireeventdata-interface "Direct link to FRCWidgetExpireEventData interface") Payload of the `"frc:widget.expire"` event. **Signature:** ``` export interface FRCWidgetExpireEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- | | [id](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.id.md) | | string | The widget ID that the event originated from. | | [name](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.name.md) | | typeof [FRCWidgetExpireEventName](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventname.md) | `"frc:widget.expire"` | | [response](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.response.md) | | string | The current `frc-captcha-response` value. | | [state](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.state.md) | | "expired" | | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) > [id](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.id.md) ## FRCWidgetExpireEventData.id property[​](#frcwidgetexpireeventdataid-property "Direct link to FRCWidgetExpireEventData.id property") The widget ID that the event originated from. **Signature:** ``` id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.name.md) ## FRCWidgetExpireEventData.name property[​](#frcwidgetexpireeventdataname-property "Direct link to FRCWidgetExpireEventData.name property") `"frc:widget.expire"` **Signature:** ``` name: typeof FRCWidgetExpireEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) > [response](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.response.md) ## FRCWidgetExpireEventData.response property[​](#frcwidgetexpireeventdataresponse-property "Direct link to FRCWidgetExpireEventData.response property") The current `frc-captcha-response` value. **Signature:** ``` response: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) > [state](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.state.md) ## FRCWidgetExpireEventData.state property[​](#frcwidgetexpireeventdatastate-property "Direct link to FRCWidgetExpireEventData.state property") **Signature:** ``` state: "expired"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetExpireEventName](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventname.md) ## FRCWidgetExpireEventName variable[​](#frcwidgetexpireeventname-variable "Direct link to FRCWidgetExpireEventName variable") `"frc:widget.expire"` **Signature:** ``` FRCWidgetExpireEventName = "frc:widget.expire" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) ## FRCWidgetResetEventData interface[​](#frcwidgetreseteventdata-interface "Direct link to FRCWidgetResetEventData interface") Payload of the `"frc:widget.reset"` event. **Signature:** ``` export interface FRCWidgetResetEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | -------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [id](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.id.md) | | string | The widget ID that the event originated from. | | [name](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.name.md) | | typeof [FRCWidgetResetEventName](/docs/v2/sdk/reference/sdk.frcwidgetreseteventname.md) | `"frc:widget.reset"` | | [response](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.response.md) | | string | The current `frc-captcha-response` value. | | [state](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.state.md) | | "reset" | | | [trigger](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.trigger.md) | | [WidgetResetTrigger](/docs/v2/sdk/reference/sdk.widgetresettrigger.md) | What caused the reset. Possible values: \* `"widget"`: reset initiated by the widget (`"widget"`), which generally means the user clicked the reset button within the widget. \* `"root"`: triggered by your own code on the current page (by calling `widget.reset()`). \* `"agent"`: triggered by the background agent (currently never happens). | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) > [id](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.id.md) ## FRCWidgetResetEventData.id property[​](#frcwidgetreseteventdataid-property "Direct link to FRCWidgetResetEventData.id property") The widget ID that the event originated from. **Signature:** ``` id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.name.md) ## FRCWidgetResetEventData.name property[​](#frcwidgetreseteventdataname-property "Direct link to FRCWidgetResetEventData.name property") `"frc:widget.reset"` **Signature:** ``` name: typeof FRCWidgetResetEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) > [response](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.response.md) ## FRCWidgetResetEventData.response property[​](#frcwidgetreseteventdataresponse-property "Direct link to FRCWidgetResetEventData.response property") The current `frc-captcha-response` value. **Signature:** ``` response: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) > [state](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.state.md) ## FRCWidgetResetEventData.state property[​](#frcwidgetreseteventdatastate-property "Direct link to FRCWidgetResetEventData.state property") **Signature:** ``` state: "reset"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) > [trigger](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.trigger.md) ## FRCWidgetResetEventData.trigger property[​](#frcwidgetreseteventdatatrigger-property "Direct link to FRCWidgetResetEventData.trigger property") What caused the reset. Possible values: \* `"widget"`: reset initiated by the widget (`"widget"`), which generally means the user clicked the reset button within the widget. \* `"root"`: triggered by your own code on the current page (by calling `widget.reset()`). \* `"agent"`: triggered by the background agent (currently never happens). **Signature:** ``` trigger: WidgetResetTrigger; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetResetEventName](/docs/v2/sdk/reference/sdk.frcwidgetreseteventname.md) ## FRCWidgetResetEventName variable[​](#frcwidgetreseteventname-variable "Direct link to FRCWidgetResetEventName variable") `"frc:widget.reset"` **Signature:** ``` FRCWidgetResetEventName = "frc:widget.reset" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEvent](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeevent.md) ## FRCWidgetStateChangeEvent type[​](#frcwidgetstatechangeevent-type "Direct link to FRCWidgetStateChangeEvent type") Event that gets dispatched when the widget enters a new state. **Signature:** ``` export type FRCWidgetStateChangeEvent = CustomEvent; ``` **References:** [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) ## FRCWidgetStateChangeEventData interface[​](#frcwidgetstatechangeeventdata-interface "Direct link to FRCWidgetStateChangeEventData interface") Payload of the `"frc:widget.statechange"` event. **Signature:** ``` export interface FRCWidgetStateChangeEventData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | -------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [error?](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.error.md) | | [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) | *(Optional)* The error that caused the state change, if any. Undefined if `state` is not equal to `"error"`. | | [id](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.id.md) | | string | The widget ID that the event originated from. | | [mode?](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.mode.md) | | [WidgetMode](/docs/v2/sdk/reference/sdk.widgetmode.md) | *(Optional)* The WidgetMode returned from the API. Smart Mode intelligently chooses between One-click Mode ("interactive") and Zero-click Mode ("noninteractive"). The mode is configured in the Friendly Captcha dashboard. | | [name](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.name.md) | | typeof [FRCWidgetStateChangeEventName](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventname.md) | `"frc:widget.statechange"` | | [response](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.response.md) | | string | The current `frc-captcha-response` value. | | [state](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.state.md) | | [WidgetState](/docs/v2/sdk/reference/sdk.widgetstate.md) | The new state of the widget. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [error](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.error.md) ## FRCWidgetStateChangeEventData.error property[​](#frcwidgetstatechangeeventdataerror-property "Direct link to FRCWidgetStateChangeEventData.error property") The error that caused the state change, if any. Undefined if `state` is not equal to `"error"`. **Signature:** ``` error?: WidgetErrorData; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [id](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.id.md) ## FRCWidgetStateChangeEventData.id property[​](#frcwidgetstatechangeeventdataid-property "Direct link to FRCWidgetStateChangeEventData.id property") The widget ID that the event originated from. **Signature:** ``` id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [mode](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.mode.md) ## FRCWidgetStateChangeEventData.mode property[​](#frcwidgetstatechangeeventdatamode-property "Direct link to FRCWidgetStateChangeEventData.mode property") The WidgetMode returned from the API. Smart Mode intelligently chooses between One-click Mode ("interactive") and Zero-click Mode ("noninteractive"). The mode is configured in the Friendly Captcha dashboard. **Signature:** ``` mode?: WidgetMode; ``` ## Remarks[​](#remarks "Direct link to Remarks") The API chooses the mode during activation, meaning that the mode will not be available prior to the `"activated"` state. In other words, `mode` will be only be present for `"activated"`, `"requesting"`, `"solving"`, `"verifying"`, `"completed"`, and `"error"`. For other states, it will be `undefined`. See the [widget lifecycle](/docs/v2/sdk/lifecycle.md) docs for more information. --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [name](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.name.md) ## FRCWidgetStateChangeEventData.name property[​](#frcwidgetstatechangeeventdataname-property "Direct link to FRCWidgetStateChangeEventData.name property") `"frc:widget.statechange"` **Signature:** ``` name: typeof FRCWidgetStateChangeEventName; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [response](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.response.md) ## FRCWidgetStateChangeEventData.response property[​](#frcwidgetstatechangeeventdataresponse-property "Direct link to FRCWidgetStateChangeEventData.response property") The current `frc-captcha-response` value. **Signature:** ``` response: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventData](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.md) > [state](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventdata.state.md) ## FRCWidgetStateChangeEventData.state property[​](#frcwidgetstatechangeeventdatastate-property "Direct link to FRCWidgetStateChangeEventData.state property") The new state of the widget. **Signature:** ``` state: WidgetState; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetStateChangeEventName](/docs/v2/sdk/reference/sdk.frcwidgetstatechangeeventname.md) ## FRCWidgetStateChangeEventName variable[​](#frcwidgetstatechangeeventname-variable "Direct link to FRCWidgetStateChangeEventName variable") `"frc:widget.statechange"` **Signature:** ``` FRCWidgetStateChangeEventName = "frc:widget.statechange" ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetWidgetErrorEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgeterrorevent.md) ## FRCWidgetWidgetErrorEvent type[​](#frcwidgetwidgeterrorevent-type "Direct link to FRCWidgetWidgetErrorEvent type") Event that gets dispatched when something goes wrong in the widget. **Signature:** ``` export type FRCWidgetWidgetErrorEvent = CustomEvent; ``` **References:** [FRCWidgetErrorEventData](/docs/v2/sdk/reference/sdk.frcwidgeterroreventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetWidgetExpireEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetexpireevent.md) ## FRCWidgetWidgetExpireEvent type[​](#frcwidgetwidgetexpireevent-type "Direct link to FRCWidgetWidgetExpireEvent type") Event that gets dispatched when the widget expires. This happens when the user takes too long to submit the captcha after it is solved. **Signature:** ``` export type FRCWidgetWidgetExpireEvent = CustomEvent; ``` **References:** [FRCWidgetExpireEventData](/docs/v2/sdk/reference/sdk.frcwidgetexpireeventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FRCWidgetWidgetResetEvent](/docs/v2/sdk/reference/sdk.frcwidgetwidgetresetevent.md) ## FRCWidgetWidgetResetEvent type[​](#frcwidgetwidgetresetevent-type "Direct link to FRCWidgetWidgetResetEvent type") Event that gets dispatched when something goes wrong in the widget. **Signature:** ``` export type FRCWidgetWidgetResetEvent = CustomEvent; ``` **References:** [FRCWidgetResetEventData](/docs/v2/sdk/reference/sdk.frcwidgetreseteventdata.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) ## FriendlyCaptchaSDK class[​](#friendlycaptchasdk-class "Direct link to FriendlyCaptchaSDK class") Main entry point for V2 of the Friendly Captcha SDK. This class keeps track of widgets and allows you to create widgets programmatically. Generally there should only be one instance of this SDK in your website. **Signature:** ``` export declare class FriendlyCaptchaSDK ``` ## Constructors[​](#constructors "Direct link to Constructors") | Constructor | Modifiers | Description | | ------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------- | | [(constructor)(opts)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk._constructor_.md) | | Constructs a new instance of the `FriendlyCaptchaSDK` class | ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | | [attached](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attached.md) | | Promise<[WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md)\[]> | A promise that resolves to all the widgets that are currently attached. | ## Methods[​](#methods "Direct link to Methods") | Method | Modifiers | Description | | ----------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [attach(elements)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attach.md) | | Attaches a widget to given element or elements if they are not attached to yet.You can pass one or more HTML elements to attach to, or undefined. If `undefined` is passed, the HTML page is scanned for unattached widget elements (= elements with the `frc-captcha` class).Returns handles to the newly-attached elements. | | [clear()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clear.md) | | Completely remove all widgets and background agents related to the SDK on this page. | | [clearRiskIntelligence(opts)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clearriskintelligence.md) | | Clears cached Risk Intelligence tokens. Cached tokens for a given sitekey can be cleared by specifying it; if a sitekey is not specified, all tokens will be cleared from the cache. | | [createWidget(opts)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.createwidget.md) | | Creates a Friendly Captcha widget with given options under given HTML element. | | [getAllRiskIntelligenceHandles()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallriskintelligencehandles.md) | | Returns all current Risk Intelligence handles known about (in an unspecified order). | | [getAllWidgets()](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallwidgets.md) | | Returns all current widgets known about (in an unspecified order). | | [getWidgetById(id)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getwidgetbyid.md) | | Retrieves a widget by its widget ID. | | [riskIntelligence(opts)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.riskintelligence.md) | | Creates a Risk Intelligence token generation request, returning a Promise that resolves to the generated token. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [(constructor)](/docs/v2/sdk/reference/sdk.friendlycaptchasdk._constructor_.md) ## FriendlyCaptchaSDK.(constructor)[​](#friendlycaptchasdkconstructor "Direct link to FriendlyCaptchaSDK.(constructor)") Constructs a new instance of the `FriendlyCaptchaSDK` class **Signature:** ``` constructor(opts?: FriendlyCaptchaSDKOptions); ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------ | ------------ | | opts | [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) | *(Optional)* | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [attach](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attach.md) ## FriendlyCaptchaSDK.attach() method[​](#friendlycaptchasdkattach-method "Direct link to FriendlyCaptchaSDK.attach() method") Attaches a widget to given element or elements if they are not attached to yet. You can pass one or more HTML elements to attach to, or undefined. If `undefined` is passed, the HTML page is scanned for unattached widget elements (= elements with the `frc-captcha` class). Returns handles to the newly-attached elements. **Signature:** ``` attach(elements?: HTMLElement | HTMLElement[] | NodeListOf): WidgetHandle[]; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ----------------------------------------------------- | ------------ | | elements | HTMLElement \| HTMLElement\[] \| NodeListOf\ | *(Optional)* | **Returns:** [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md)\[] --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [attached](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.attached.md) ## FriendlyCaptchaSDK.attached property[​](#friendlycaptchasdkattached-property "Direct link to FriendlyCaptchaSDK.attached property") A promise that resolves to all the widgets that are currently attached. **Signature:** ``` attached: Promise; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [clear](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clear.md) ## FriendlyCaptchaSDK.clear() method[​](#friendlycaptchasdkclear-method "Direct link to FriendlyCaptchaSDK.clear() method") Completely remove all widgets and background agents related to the SDK on this page. **Signature:** ``` clear(): void; ``` **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [clearRiskIntelligence](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.clearriskintelligence.md) ## FriendlyCaptchaSDK.clearRiskIntelligence() method[​](#friendlycaptchasdkclearriskintelligence-method "Direct link to FriendlyCaptchaSDK.clearRiskIntelligence() method") Clears cached Risk Intelligence tokens. Cached tokens for a given sitekey can be cleared by specifying it; if a sitekey is not specified, all tokens will be cleared from the cache. **Signature:** ``` clearRiskIntelligence(opts?: RiskIntelligenceClearOptions): Promise; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------------ | ------------ | | opts | [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md) | *(Optional)* | **Returns:** Promise\ --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [createWidget](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.createwidget.md) ## FriendlyCaptchaSDK.createWidget() method[​](#friendlycaptchasdkcreatewidget-method "Direct link to FriendlyCaptchaSDK.createWidget() method") Creates a Friendly Captcha widget with given options under given HTML element. **Signature:** ``` createWidget(opts: CreateWidgetOptions): WidgetHandle; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------ | ----------- | | opts | [CreateWidgetOptions](/docs/v2/sdk/reference/sdk.createwidgetoptions.md) | | **Returns:** [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [getAllRiskIntelligenceHandles](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallriskintelligencehandles.md) ## FriendlyCaptchaSDK.getAllRiskIntelligenceHandles() method[​](#friendlycaptchasdkgetallriskintelligencehandles-method "Direct link to FriendlyCaptchaSDK.getAllRiskIntelligenceHandles() method") Returns all current Risk Intelligence handles known about (in an unspecified order). **Signature:** ``` getAllRiskIntelligenceHandles(): RiskIntelligenceHandle[]; ``` **Returns:** [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md)\[] --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [getAllWidgets](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getallwidgets.md) ## FriendlyCaptchaSDK.getAllWidgets() method[​](#friendlycaptchasdkgetallwidgets-method "Direct link to FriendlyCaptchaSDK.getAllWidgets() method") Returns all current widgets known about (in an unspecified order). **Signature:** ``` getAllWidgets(): WidgetHandle[]; ``` **Returns:** [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md)\[] --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [getWidgetById](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.getwidgetbyid.md) ## FriendlyCaptchaSDK.getWidgetById() method[​](#friendlycaptchasdkgetwidgetbyid-method "Direct link to FriendlyCaptchaSDK.getWidgetById() method") Retrieves a widget by its widget ID. **Signature:** ``` getWidgetById(id: string): WidgetHandle | undefined; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------ | ----------- | | id | string | | **Returns:** [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) | undefined --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDK](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.md) > [riskIntelligence](/docs/v2/sdk/reference/sdk.friendlycaptchasdk.riskintelligence.md) ## FriendlyCaptchaSDK.riskIntelligence() method[​](#friendlycaptchasdkriskintelligence-method "Direct link to FriendlyCaptchaSDK.riskIntelligence() method") Creates a Risk Intelligence token generation request, returning a Promise that resolves to the generated token. **Signature:** ``` riskIntelligence(opts: RiskIntelligenceOptions): Promise; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | -------------------------------------------------------------------------------- | ----------- | | opts | [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) | | **Returns:** Promise<[RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md)> --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) ## FriendlyCaptchaSDKOptions interface[​](#friendlycaptchasdkoptions-interface "Direct link to FriendlyCaptchaSDKOptions interface") Options when creating a new SDK instance. **Signature:** ``` export interface FriendlyCaptchaSDKOptions ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [apiEndpoint?](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.apiendpoint.md) | | [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) | *(Optional)* The API endpoint to use, defaults to `https://global.frcapi.com`.Supports the following shortcuts: - `eu` - `https://eu.frcapi.com` - `global` - `https://global.frcapi.com` | | [disableEvalPatching?](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.disableevalpatching.md) | | boolean | *(Optional)* Whether to disable the patching of `window.eval`. Useful when the patching breaks your site, which in particular may affect some hot reloading functionality for Webpack (in `dev` mode). | | [startAgent?](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.startagent.md) | | boolean | *(Optional)* Start the background agent (and solver) immediately, defaults to `true`. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) > [apiEndpoint](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.apiendpoint.md) ## FriendlyCaptchaSDKOptions.apiEndpoint property[​](#friendlycaptchasdkoptionsapiendpoint-property "Direct link to FriendlyCaptchaSDKOptions.apiEndpoint property") The API endpoint to use, defaults to `https://global.frcapi.com`. Supports the following shortcuts: - `eu` - `https://eu.frcapi.com` - `global` - `https://global.frcapi.com` **Signature:** ``` apiEndpoint?: APIEndpoint; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) > [disableEvalPatching](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.disableevalpatching.md) ## FriendlyCaptchaSDKOptions.disableEvalPatching property[​](#friendlycaptchasdkoptionsdisableevalpatching-property "Direct link to FriendlyCaptchaSDKOptions.disableEvalPatching property") Whether to disable the patching of `window.eval`. Useful when the patching breaks your site, which in particular may affect some hot reloading functionality for Webpack (in `dev` mode). **Signature:** ``` disableEvalPatching?: boolean; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [FriendlyCaptchaSDKOptions](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.md) > [startAgent](/docs/v2/sdk/reference/sdk.friendlycaptchasdkoptions.startagent.md) ## FriendlyCaptchaSDKOptions.startAgent property[​](#friendlycaptchasdkoptionsstartagent-property "Direct link to FriendlyCaptchaSDKOptions.startAgent property") Start the background agent (and solver) immediately, defaults to `true`. **Signature:** ``` startAgent?: boolean; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md) ## RiskIntelligenceClearOptions interface[​](#riskintelligenceclearoptions-interface "Direct link to RiskIntelligenceClearOptions interface") Options for clearing cached Risk Intelligence tokens. **Signature:** ``` export interface RiskIntelligenceClearOptions ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | -------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [apiEndpoint?](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.apiendpoint.md) | | [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) | *(Optional)* Tokens generated by this API endpoint will be cleared. | | [sitekey?](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.sitekey.md) | | string | *(Optional)* Sitekey of your application, starts with `FC`. Tokens associated with this application will be cleared. If not passed, all tokens will be cleared. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md) > [apiEndpoint](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.apiendpoint.md) ## RiskIntelligenceClearOptions.apiEndpoint property[​](#riskintelligenceclearoptionsapiendpoint-property "Direct link to RiskIntelligenceClearOptions.apiEndpoint property") Tokens generated by this API endpoint will be cleared. **Signature:** ``` apiEndpoint?: APIEndpoint; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceClearOptions](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.md) > [sitekey](/docs/v2/sdk/reference/sdk.riskintelligenceclearoptions.sitekey.md) ## RiskIntelligenceClearOptions.sitekey property[​](#riskintelligenceclearoptionssitekey-property "Direct link to RiskIntelligenceClearOptions.sitekey property") Sitekey of your application, starts with `FC`. Tokens associated with this application will be cleared. If not passed, all tokens will be cleared. **Signature:** ``` sitekey?: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceErrorCode](/docs/v2/sdk/reference/sdk.riskintelligenceerrorcode.md) ## RiskIntelligenceErrorCode type[​](#riskintelligenceerrorcode-type "Direct link to RiskIntelligenceErrorCode type") Error codes that can be returned by the Risk Intelligence request. See [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) for the list of possible error codes. **Signature:** ``` export type RiskIntelligenceErrorCode = WidgetErrorCode; ``` **References:** [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md) ## RiskIntelligenceErrorData interface[​](#riskintelligenceerrordata-interface "Direct link to RiskIntelligenceErrorData interface") Error data returned by a failed Risk Intelligence request. **Signature:** ``` export interface RiskIntelligenceErrorData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | ------------------------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [code](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.code.md) | | [RiskIntelligenceErrorCode](/docs/v2/sdk/reference/sdk.riskintelligenceerrorcode.md) | The error code. | | [detail](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.detail.md) | | string | More details about the error to help debugging. This value is not localized and will change between versions.You can print this to the console, but make sure not to depend on it in your code. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md) > [code](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.code.md) ## RiskIntelligenceErrorData.code property[​](#riskintelligenceerrordatacode-property "Direct link to RiskIntelligenceErrorData.code property") The error code. **Signature:** ``` code: RiskIntelligenceErrorCode; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceErrorData](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.md) > [detail](/docs/v2/sdk/reference/sdk.riskintelligenceerrordata.detail.md) ## RiskIntelligenceErrorData.detail property[​](#riskintelligenceerrordatadetail-property "Direct link to RiskIntelligenceErrorData.detail property") More details about the error to help debugging. This value is not localized and will change between versions. You can print this to the console, but make sure not to depend on it in your code. **Signature:** ``` detail: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md) ## RiskIntelligenceGenerateData interface[​](#riskintelligencegeneratedata-interface "Direct link to RiskIntelligenceGenerateData interface") Data returned by the Risk Intelligence API request. **Signature:** ``` export interface RiskIntelligenceGenerateData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------------------- | --------- | ------ | ----------------------------------------------------------------------------------------------------------- | | [expiresAt](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.expiresat.md) | | number | A timestamp, represented as a Unix epoch, for when the Risk Intelligence token will expire. | | [token](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.token.md) | | string | The Risk Intelligence token, which can be used to retrieve Risk Intelligence from the Friendly Captcha API. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md) > [expiresAt](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.expiresat.md) ## RiskIntelligenceGenerateData.expiresAt property[​](#riskintelligencegeneratedataexpiresat-property "Direct link to RiskIntelligenceGenerateData.expiresAt property") A timestamp, represented as a Unix epoch, for when the Risk Intelligence token will expire. **Signature:** ``` expiresAt: number; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md) > [token](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.token.md) ## RiskIntelligenceGenerateData.token property[​](#riskintelligencegeneratedatatoken-property "Direct link to RiskIntelligenceGenerateData.token property") The Risk Intelligence token, which can be used to retrieve Risk Intelligence from the Friendly Captcha API. **Signature:** ``` token: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) ## RiskIntelligenceHandle class[​](#riskintelligencehandle-class "Direct link to RiskIntelligenceHandle class") This provides a handle for configuring and managing a Risk Intelligence request via an HTML element. This class is only instantiated by the SDK - do not create a handle yourself. **Signature:** ``` export declare class RiskIntelligenceHandle ``` ## Remarks[​](#remarks "Direct link to Remarks") The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `RiskIntelligenceHandle` class. ## Methods[​](#methods "Direct link to Methods") | Method | Modifiers | Description | | ------------------------------------------------------------------------------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------- | | [addEventListener(type, listener, options)](/docs/v2/sdk/reference/sdk.riskintelligencehandle.addeventlistener.md) | | Shorthand for `this.getElement().addEventListener` (that is strictly typed in Typescript) | | [getData()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getdata.md) | | | | [getElement()](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getelement.md) | | | | [removeEventListener(type, listener, options)](/docs/v2/sdk/reference/sdk.riskintelligencehandle.removeeventlistener.md) | | Shorthand for `this.getElement().removeEventListener` (that is strictly typed in Typescript) | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) > [addEventListener](/docs/v2/sdk/reference/sdk.riskintelligencehandle.addeventlistener.md) ## RiskIntelligenceHandle.addEventListener() method[​](#riskintelligencehandleaddeventlistener-method "Direct link to RiskIntelligenceHandle.addEventListener() method") Shorthand for `this.getElement().addEventListener` (that is strictly typed in Typescript) **Signature:** ``` addEventListener(type: K, listener: (this: HTMLElement, ev: FRCEventMap[K]) => any | { handleEvent: (ev: FRCEventMap[K]) => any; }, options?: AddEventListenerOptions): void; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | type | K | | | listener | (this: HTMLElement, ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any \| { handleEvent: (ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any; } | | | options | AddEventListenerOptions | *(Optional)* | **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) > [getData](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getdata.md) ## RiskIntelligenceHandle.getData() method[​](#riskintelligencehandlegetdata-method "Direct link to RiskIntelligenceHandle.getData() method") **Signature:** ``` getData(): RiskIntelligenceGenerateData | null; ``` **Returns:** [RiskIntelligenceGenerateData](/docs/v2/sdk/reference/sdk.riskintelligencegeneratedata.md) | null Risk Intelligence data if request is done and `null` if not. --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) > [getElement](/docs/v2/sdk/reference/sdk.riskintelligencehandle.getelement.md) ## RiskIntelligenceHandle.getElement() method[​](#riskintelligencehandlegetelement-method "Direct link to RiskIntelligenceHandle.getElement() method") **Signature:** ``` getElement(): HTMLElement; ``` **Returns:** HTMLElement The HTML element used to configure the Risk Intelligence request. --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceHandle](/docs/v2/sdk/reference/sdk.riskintelligencehandle.md) > [removeEventListener](/docs/v2/sdk/reference/sdk.riskintelligencehandle.removeeventlistener.md) ## RiskIntelligenceHandle.removeEventListener() method[​](#riskintelligencehandleremoveeventlistener-method "Direct link to RiskIntelligenceHandle.removeEventListener() method") Shorthand for `this.getElement().removeEventListener` (that is strictly typed in Typescript) **Signature:** ``` removeEventListener(type: K, listener: (this: HTMLElement, ev: FRCEventMap[K]) => any | { handleEvent: (ev: FRCEventMap[K]) => any; }, options?: EventListenerOptions): void; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | type | K | | | listener | (this: HTMLElement, ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any \| { handleEvent: (ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any; } | | | options | EventListenerOptions | *(Optional)* | **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) ## RiskIntelligenceOptions interface[​](#riskintelligenceoptions-interface "Direct link to RiskIntelligenceOptions interface") Options for configuring a Risk Intelligence request. **Signature:** ``` export interface RiskIntelligenceOptions ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | [apiEndpoint?](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.apiendpoint.md) | | [APIEndpoint](/docs/v2/sdk/reference/sdk.apiendpoint.md) | *(Optional)* A custom endpoint from which the agent is loaded and to which Risk Intelligence requests are made. | | [bypassCache?](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.bypasscache.md) | | boolean | *(Optional)* Whether to bypass the token cache and force the request of a new token from the API. | | [sitekey](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.sitekey.md) | | string | Sitekey of your application, starts with `FC`. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) > [apiEndpoint](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.apiendpoint.md) ## RiskIntelligenceOptions.apiEndpoint property[​](#riskintelligenceoptionsapiendpoint-property "Direct link to RiskIntelligenceOptions.apiEndpoint property") A custom endpoint from which the agent is loaded and to which Risk Intelligence requests are made. **Signature:** ``` apiEndpoint?: APIEndpoint; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) > [bypassCache](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.bypasscache.md) ## RiskIntelligenceOptions.bypassCache property[​](#riskintelligenceoptionsbypasscache-property "Direct link to RiskIntelligenceOptions.bypassCache property") Whether to bypass the token cache and force the request of a new token from the API. **Signature:** ``` bypassCache?: boolean; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [RiskIntelligenceOptions](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.md) > [sitekey](/docs/v2/sdk/reference/sdk.riskintelligenceoptions.sitekey.md) ## RiskIntelligenceOptions.sitekey property[​](#riskintelligenceoptionssitekey-property "Direct link to RiskIntelligenceOptions.sitekey property") Sitekey of your application, starts with `FC`. **Signature:** ``` sitekey: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [SentinelResponse](/docs/v2/sdk/reference/sdk.sentinelresponse.md) ## SentinelResponse type[​](#sentinelresponse-type "Direct link to SentinelResponse type") Response values used in the hidden input field when no valid solution is present, these tell you something about the state of the widget. **Signature:** ``` export type SentinelResponse = ".UNINITIALIZED" | ".UNCONNECTED" | ".UNSTARTED" | ".REQUESTING" | ".SOLVING" | ".VERIFYING" | ".EXPIRED" | ".DESTROYED" | ".ERROR" | ".ERROR.UNREACHABLE" | ".RESET"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [StartMode](/docs/v2/sdk/reference/sdk.startmode.md) ## StartMode type[​](#startmode-type "Direct link to StartMode type") The start mode of the widget. \* `"auto"`: the widget gets activated as soon as it is created. \* `"focus"`: the widget gets activated as soon as the form above it is focused. \* `"none"`: The widget does not get activated automatically at all, the user needs to press the widget (or `.start()` gets called using the Javascript API). **Signature:** ``` export type StartMode = "focus" | "auto" | "none"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) ## WidgetErrorCode type[​](#widgeterrorcode-type "Direct link to WidgetErrorCode type") Error codes that can be returned by the widget. \* `"network_error"`: The user's browser could not connect to the Friendly Captcha API. \* `"sitekey_invalid"`: The sitekey is invalid. \* `"sitekey_missing"`: The sitekey is missing. \* `"other"`: Some other error occurred. In all cases it's the best practice to enable the "submit" button when the widget errors, so that the user can still perform the action despite not having solved the captcha. **Signature:** ``` export type WidgetErrorCode = "network_error" | "sitekey_invalid" | "sitekey_missing" | "other"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) ## WidgetErrorData interface[​](#widgeterrordata-interface "Direct link to WidgetErrorData interface") **Signature:** ``` export interface WidgetErrorData ``` ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | -------------------------------------------------------------- | --------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [code](/docs/v2/sdk/reference/sdk.widgeterrordata.code.md) | | [WidgetErrorCode](/docs/v2/sdk/reference/sdk.widgeterrorcode.md) | The error code. | | [detail](/docs/v2/sdk/reference/sdk.widgeterrordata.detail.md) | | string | More details about the error to help debugging. This value is not localized and will change between versions.You can print this to the console, but make sure not to depend on it in your code. | | [title?](/docs/v2/sdk/reference/sdk.widgeterrordata.title.md) | | string | *(Optional)* Localization key, such as `t_verification_failed`. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) > [code](/docs/v2/sdk/reference/sdk.widgeterrordata.code.md) ## WidgetErrorData.code property[​](#widgeterrordatacode-property "Direct link to WidgetErrorData.code property") The error code. **Signature:** ``` code: WidgetErrorCode; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) > [detail](/docs/v2/sdk/reference/sdk.widgeterrordata.detail.md) ## WidgetErrorData.detail property[​](#widgeterrordatadetail-property "Direct link to WidgetErrorData.detail property") More details about the error to help debugging. This value is not localized and will change between versions. You can print this to the console, but make sure not to depend on it in your code. **Signature:** ``` detail: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetErrorData](/docs/v2/sdk/reference/sdk.widgeterrordata.md) > [title](/docs/v2/sdk/reference/sdk.widgeterrordata.title.md) ## WidgetErrorData.title property[​](#widgeterrordatatitle-property "Direct link to WidgetErrorData.title property") Localization key, such as `t_verification_failed`. **Signature:** ``` title?: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) ## WidgetHandle class[​](#widgethandle-class "Direct link to WidgetHandle class") This provides an API stub that provides the end-user JS API for a widget. This class is only instantiated by the SDK - do not create a handle yourself. **Signature:** ``` export declare class WidgetHandle ``` ## Remarks[​](#remarks "Direct link to Remarks") The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `WidgetHandle` class. ## Properties[​](#properties "Direct link to Properties") | Property | Modifiers | Type | Description | | --------------------------------------------------------------------- | ---------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------- | | [id](/docs/v2/sdk/reference/sdk.widgethandle.id.md) | `readonly` | string | A random ID that uniquely identifies this widget in this session. | | [isDestroyed](/docs/v2/sdk/reference/sdk.widgethandle.isdestroyed.md) | | boolean | When this is true the widget has been destroyed and can no longer be used. | | [sitekey?](/docs/v2/sdk/reference/sdk.widgethandle.sitekey.md) | `readonly` | string | *(Optional)* The sitekey for this widget. It can not be changed after creation of the widget. | | [startMode](/docs/v2/sdk/reference/sdk.widgethandle.startmode.md) | | [StartMode](/docs/v2/sdk/reference/sdk.startmode.md) | | ## Methods[​](#methods "Direct link to Methods") | Method | Modifiers | Description | | -------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [addEventListener(type, listener, options)](/docs/v2/sdk/reference/sdk.widgethandle.addeventlistener.md) | | Shorthand for `this.getElement().addEventListener` (that is strictly typed in Typescript) | | [destroy()](/docs/v2/sdk/reference/sdk.widgethandle.destroy.md) | | Destroy the widget.This removes the `element` that the widget was mounted to as well as the hidden `frc-captcha-response` form field. | | [getElement()](/docs/v2/sdk/reference/sdk.widgethandle.getelement.md) | | The HTML element that contains the widget. | | [getResponse()](/docs/v2/sdk/reference/sdk.widgethandle.getresponse.md) | | The current response of the widget. This is the value that should be sent to the server and is embedded in HTML forms. | | [getState()](/docs/v2/sdk/reference/sdk.widgethandle.getstate.md) | | The current state of the widget. | | [removeEventListener(type, listener, options)](/docs/v2/sdk/reference/sdk.widgethandle.removeeventlistener.md) | | Shorthand for `this.getElement().removeEventListener` (that is strictly typed in Typescript) | | [reset(opts)](/docs/v2/sdk/reference/sdk.widgethandle.reset.md) | | Reset the widget, removing any progress.Optional argument: an object with the name of the trigger that caused the reset. You would usually keep this empty. This is the `trigger` field in the `frc:widget.reset` event, which defaults to `root`. | | [start()](/docs/v2/sdk/reference/sdk.widgethandle.start.md) | | Trigger the widget to start a challenge. The widget will start a challenge solving in the background.\* In `interactive` mode, the user will need to click the widget to complete the process. \* In `noninteractive` mode, the widget will complete the process automatically. | --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [addEventListener](/docs/v2/sdk/reference/sdk.widgethandle.addeventlistener.md) ## WidgetHandle.addEventListener() method[​](#widgethandleaddeventlistener-method "Direct link to WidgetHandle.addEventListener() method") Shorthand for `this.getElement().addEventListener` (that is strictly typed in Typescript) **Signature:** ``` addEventListener(type: K, listener: (this: HTMLElement, ev: FRCEventMap[K]) => any | { handleEvent: (ev: FRCEventMap[K]) => any; }, options?: AddEventListenerOptions): void; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | type | K | | | listener | (this: HTMLElement, ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any \| { handleEvent: (ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any; } | | | options | AddEventListenerOptions | *(Optional)* | **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [destroy](/docs/v2/sdk/reference/sdk.widgethandle.destroy.md) ## WidgetHandle.destroy() method[​](#widgethandledestroy-method "Direct link to WidgetHandle.destroy() method") Destroy the widget. This removes the `element` that the widget was mounted to as well as the hidden `frc-captcha-response` form field. **Signature:** ``` destroy(): void; ``` **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [getElement](/docs/v2/sdk/reference/sdk.widgethandle.getelement.md) ## WidgetHandle.getElement() method[​](#widgethandlegetelement-method "Direct link to WidgetHandle.getElement() method") The HTML element that contains the widget. **Signature:** ``` getElement(): HTMLElement; ``` **Returns:** HTMLElement --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [getResponse](/docs/v2/sdk/reference/sdk.widgethandle.getresponse.md) ## WidgetHandle.getResponse() method[​](#widgethandlegetresponse-method "Direct link to WidgetHandle.getResponse() method") The current response of the widget. This is the value that should be sent to the server and is embedded in HTML forms. **Signature:** ``` getResponse(): string; ``` **Returns:** string --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [getState](/docs/v2/sdk/reference/sdk.widgethandle.getstate.md) ## WidgetHandle.getState() method[​](#widgethandlegetstate-method "Direct link to WidgetHandle.getState() method") The current state of the widget. **Signature:** ``` getState(): WidgetState; ``` **Returns:** [WidgetState](/docs/v2/sdk/reference/sdk.widgetstate.md) --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [id](/docs/v2/sdk/reference/sdk.widgethandle.id.md) ## WidgetHandle.id property[​](#widgethandleid-property "Direct link to WidgetHandle.id property") A random ID that uniquely identifies this widget in this session. **Signature:** ``` readonly id: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [isDestroyed](/docs/v2/sdk/reference/sdk.widgethandle.isdestroyed.md) ## WidgetHandle.isDestroyed property[​](#widgethandleisdestroyed-property "Direct link to WidgetHandle.isDestroyed property") When this is true the widget has been destroyed and can no longer be used. **Signature:** ``` isDestroyed: boolean; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [removeEventListener](/docs/v2/sdk/reference/sdk.widgethandle.removeeventlistener.md) ## WidgetHandle.removeEventListener() method[​](#widgethandleremoveeventlistener-method "Direct link to WidgetHandle.removeEventListener() method") Shorthand for `this.getElement().removeEventListener` (that is strictly typed in Typescript) **Signature:** ``` removeEventListener(type: K, listener: (this: HTMLElement, ev: FRCEventMap[K]) => any | { handleEvent: (ev: FRCEventMap[K]) => any; }, options?: EventListenerOptions): void; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | type | K | | | listener | (this: HTMLElement, ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any \| { handleEvent: (ev: [FRCEventMap](/docs/v2/sdk/reference/sdk.frceventmap.md)\[K]) => any; } | | | options | EventListenerOptions | *(Optional)* | **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [reset](/docs/v2/sdk/reference/sdk.widgethandle.reset.md) ## WidgetHandle.reset() method[​](#widgethandlereset-method "Direct link to WidgetHandle.reset() method") Reset the widget, removing any progress. Optional argument: an object with the name of the trigger that caused the reset. You would usually keep this empty. This is the `trigger` field in the `frc:widget.reset` event, which defaults to `root`. **Signature:** ``` reset(opts?: WidgetResetOptions): void; ``` ## Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | ---------------------------------------------------------------------- | ------------ | | opts | [WidgetResetOptions](/docs/v2/sdk/reference/sdk.widgetresetoptions.md) | *(Optional)* | **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [sitekey](/docs/v2/sdk/reference/sdk.widgethandle.sitekey.md) ## WidgetHandle.sitekey property[​](#widgethandlesitekey-property "Direct link to WidgetHandle.sitekey property") The sitekey for this widget. It can not be changed after creation of the widget. **Signature:** ``` readonly sitekey?: string; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [start](/docs/v2/sdk/reference/sdk.widgethandle.start.md) ## WidgetHandle.start() method[​](#widgethandlestart-method "Direct link to WidgetHandle.start() method") Trigger the widget to start a challenge. The widget will start a challenge solving in the background. \* In `interactive` mode, the user will need to click the widget to complete the process. \* In `noninteractive` mode, the widget will complete the process automatically. **Signature:** ``` start(): void; ``` **Returns:** void --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetHandle](/docs/v2/sdk/reference/sdk.widgethandle.md) > [startMode](/docs/v2/sdk/reference/sdk.widgethandle.startmode.md) ## WidgetHandle.startMode property[​](#widgethandlestartmode-property "Direct link to WidgetHandle.startMode property") **Signature:** ``` startMode: StartMode; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetMode](/docs/v2/sdk/reference/sdk.widgetmode.md) ## WidgetMode type[​](#widgetmode-type "Direct link to WidgetMode type") The mode of the widget. \* `"interactive"` (default): the widget is interactive: the user needs to click the checkbox to finalize the captcha. This mode offers the best anti-bot protection. \* `"noninteractive"`: the widget is non-interactive: the captcha is solved without any user interaction required. **Signature:** ``` export type WidgetMode = "interactive" | "noninteractive"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetResetOptions](/docs/v2/sdk/reference/sdk.widgetresetoptions.md) ## WidgetResetOptions interface[​](#widgetresetoptions-interface "Direct link to WidgetResetOptions interface") The options object you can pass to the `widget.reset()` method. **Signature:** ``` export interface WidgetResetOptions ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetResetTrigger](/docs/v2/sdk/reference/sdk.widgetresettrigger.md) ## WidgetResetTrigger type[​](#widgetresettrigger-type "Direct link to WidgetResetTrigger type") What caused the widget to reset. \* `root`: Code on the root page (= your website code or plugin) caused the reset. \* `widget`: The reset came from the widget. The user likely clicked the reset button within the widget. \* `agent`: The reset came from the agent - this currently does not happen but may in the future. **Signature:** ``` export type WidgetResetTrigger = "widget" | "root" | "agent"; ``` --- [@friendlycaptcha/sdk](/docs/v2/sdk/reference/sdk.md) > [WidgetState](/docs/v2/sdk/reference/sdk.widgetstate.md) ## WidgetState type[​](#widgetstate-type "Direct link to WidgetState type") The state the widget is in. See the [widget lifecycle](/docs/v2/sdk/lifecycle.md) docs for more information. **Signature:** ``` export type WidgetState = "init" | "reset" | "unactivated" | "activating" | "activated" | "requesting" | "solving" | "verifying" | "completed" | "expired" | "error" | "destroyed"; ``` --- ## [📄️ Brute Force Attacks](/docs/v2/use-cases/brute-force-attacks.md) [A brute force attack, also known as brute forcing, is an attempt to guess a web user’s credentials by trying every possible combination of characters. For websites and applications, the end goal of a brute force attack is account takeover (ATO), where the attacker gains unauthorized access to a different user's account.](/docs/v2/use-cases/brute-force-attacks.md) --- # Account Takeover Account takeover (ATO) is a type of identity theft where an attacker gains unauthorized access to a user's account. This can lead to various malicious activities, such as stealing personal information, making unauthorized transactions, or using the account for further attacks. ATO attacks often occur through methods like phishing, credential stuffing, or brute force attacks. ## Preventing ATO even if the user's credentials are compromised[​](#preventing-ato-even-if-the-users-credentials-are-compromised "Direct link to Preventing ATO even if the user's credentials are compromised") You can implement additional layers of security beyond just relying on passwords. You may add multi-factor authentication (MFA), which requires users to provide an additional form of verification (like a code sent to their phone) in addition to their password. This can significantly reduce the risk of account takeover, as it adds an extra barrier for attackers. But what if MFA is not an option for your users, or if you are concerned with the increased friction that MFA can cause for legitimate users? In that case, you can implement a **Risk-Based Authentication** (RBA) approach. With Risk-Based Authentication you require additional verification only for requests that are deemed risky. This way, you can protect against account takeover while still allowing legitimate users to access their accounts without unnecessary friction. [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) is a product that allows you to implement risk-based authentication in a matter of hours instead of weeks. Friendly Captcha assesses the visitor, sharing risk scores and information about the browsing session with your backend, so you can make informed decisions about how to handle requests. You could for example have a policy where you require MFA for requests that have a high risk score, but allow requests with a low risk score to proceed without additional verification. Alternatively you can store information about the user's previous browsing sessions, and if a request comes in that deviates significantly from the user's normal behavior (e.g. a login attempt from a new device or location), you can require additional verification for that request. ![Example of a Risk-Based Authentication verification email](/mockups/risk-auth-email.png) *Example verification email shown for a risky login attempt from a new location.* ## Passive monitoring for account takeover[​](#passive-monitoring-for-account-takeover "Direct link to Passive monitoring for account takeover") Even if you don't want to implement risk-based authentication, you can still use Risk Intelligence for passive monitoring of account takeover attempts. By logging signals associated with critical user interactions, you can analyze patterns of behavior and identify potential security threats. For example, you might notice a spike in high-risk scores from a particular network, geographic region or device type, which could indicate a coordinated attack. You can feed this data into your SIEM (Security Information Event Management) system to correlate it with other security events. ## Gettings Started[​](#gettings-started "Direct link to Gettings Started") To learn more about risk-based authentication in general, the [riskbasedauthentication.org](https://riskbasedauthentication.org/) website is a great resource. To get started with implementing risk-based authentication using Friendly Captcha's Risk Intelligence, you can follow our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide to set this up. --- # API Protection Your websites and apps likely rely on APIs to function. These APIs are vulnerable to abuse and attacks, which can lead to data breaches, service disruptions, and other security issues. While your APIs are intended for use by your users, an attacker may try to exploit them for malicious purposes. For example, they might use your API to scrape data, perform denial-of-service (DoS) attacks, or automate fraudulent activities. Friendly Captcha helps prevent abuse and attacks on your APIs by adding a layer of protection that can distinguish between legitimate users and malicious actors. This makes attacks more difficult and costly for attackers, while allowing legitimate users to access your services without friction. ## How can Friendly Captcha protect your APIs?[​](#how-can-friendly-captcha-protect-your-apis "Direct link to How can Friendly Captcha protect your APIs?") Friendly Captcha's challenge (the widget) is great for protecting forms and other user interactions on your website or app. But what about protecting your APIs, which may not have a user interface? [**Risk Intelligence**](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) is a product we launched in 2026 that allows you to protect API endpoints that happen entirely in the background. Friendly Captcha assesses the visitor, sharing risk scores and signals with your backend, so you can make informed decisions about how to handle requests. For example, you might choose to block requests that have a high risk score, you may rate limit them more aggressively, or you might require additional verification for those requests. This way, you can protect your APIs from abuse while still allowing legitimate users to access your services. ## What about machine-to-machine communication?[​](#what-about-machine-to-machine-communication "Direct link to What about machine-to-machine communication?") Friendly Captcha's Risk Intelligence is designed to protect APIs that are accessed by users. You can assess the risk of machine-to-machine (M2M) communication based on the IP address of the request. We launched the [**IP Trust**](https://iptrust.co/) product to help with this usecase. That service allows you to look up information about an IP address, such as whether it's a known proxy or VPN, its geolocation, and more. This can help you make informed decisions about how to handle requests from that IP address. Other than most competitors, IP Trust offers a downloadable database, which means you can use it to protect M2M communication without needing to make an API call for each request. This can be especially useful for high-volume APIs where making an API call for each request would be impractical, or if you are concerned about the privacy implications of sharing your users' IP addresses with a third party. --- # Brute Force Attacks A **brute force attack**, also known as **brute forcing**, is an attempt to guess a web user’s credentials by trying every possible combination of characters. For websites and applications, the end goal of a brute force attack is **account takeover (ATO)**, where the attacker gains unauthorized access to a different user's account. Common defenses against brute forcing include rate-limiting, which slows the rate at which attackers can attempt logins, and password complexity requirements, which increase the number of possibilities that attackers need to try. Both of these defenses aim to make brute force attacks more *costly*. Friendly Captcha’s protection also increases the cost of carrying out a brute force attack, and it includes logic to specifically identify and mitigate these kinds of attacks. ## How can Friendly Captcha protect against brute force attacks?[​](#how-can-friendly-captcha-protect-against-brute-force-attacks "Direct link to How can Friendly Captcha protect against brute force attacks?") Brute force attacks generally involve thousands to millions of requests, meaning automated browsers and scripts need to be used to attempt to break through a login form by submitting it repeatedly with different credentials. The goal is generally to make this automation more difficult and prohibitively expensive to execute to the point of being infeasible. Friendly Captcha achieves this by generating cryptographic challenges that must be solved by the browser in order to successfully submit a form. By using signals gathered from a browsing session, it makes an assessment for how likely the given request is to be abusive. It then varies the difficulty of the challenges, increasing or decreasing the amount of time it takes to complete them. We designed Friendly Captcha to be included in login forms, and the first step to mitigating a brute force attack is to install the widget in the form. We outline the steps to installing the Friendly Captcha widget in our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) article. Friendly Captcha works by generating cryptographic challenges that must be solved by the browser in order to successfully submit a form. By using signals gathered from a browsing session, it varies the difficulty of the challenges, increasing or decreasing the amount of time it takes to complete them. To combat brute forcing, the goal is to make the challenges challenging enough that the attack becomes too expensive. Let’s take a look at two signals Friendly Captcha uses that are relevant in the defense against brute force attacks. ### Rate of requests[​](#rate-of-requests "Direct link to Rate of requests") In the past, brute force attacks were simpler—perhaps a single machine targeting a website. This rudimentary approach could be blocked by rate-limiting requests from the IP address of the attacking machine. Nowadays, attackers use botnets comprising millions of machines, each with a distinct IP address, to evade IP-based rate limits. Proxy networks offer another way to bypass IP-based rate limits by allowing attackers to rotate through millions of IP addresses. Friendly Captcha uses a combination of data to generate a unique, yet anonymized, identifier that it associates with form submission requests. Each time it receives a request from a given identifier, the challenge difficulty increases. For a brute force attack, which relies on submitting a form repeatedly, the challenge difficulty will quickly become too challenging to solve in a reasonable amount of time. ### Browser information[​](#browser-information "Direct link to Browser information") These days, brute forcers often make use of browser automation technologies like [Puppeteer](https://pptr.dev/) or [Playwright](https://playwright.dev/), because they speed up the rate at which attackers can try passwords. These tools, or derivations of them, may attempt to conceal their automated nature. But they often leave traces that give clues to their identity. Friendly Captcha’s signal analysis pipeline can leverage these clues to recognize browsing sessions that are likely malicious. From there, it increases the challenge difficulty, furthering slowing down the attack. ## Brute force attacks continuously evolve[​](#brute-force-attacks-continuously-evolve "Direct link to Brute force attacks continuously evolve") A central problem in cybersecurity is that the threat landscape is changing in realtime. What worked yesterday might not work today. In order to remain secure in that landscape, your defense needs to evolve as quickly as the attackers. Friendly Captcha closely monitors the shifting trends in online security and its threats. The service constantly upgrades to provide the best protection possible while remaining unobtrusive for your real users. In addition, we’ve worked hard to make integration as simple as possible. You can add Friendly Captcha to your login flow by following the [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide. --- # Credential Stuffing Credential stuffing is a cyberattack technique where attackers use automated tools to try large numbers of username and password combinations, often obtained from previous data breaches, to gain unauthorized access to user accounts. The goal is to exploit the fact that many people reuse passwords across multiple sites, allowing attackers to compromise accounts on various platforms using the same credentials. This attack involves the use of bots to rapidly test these credentials against a target website or application, much like in a [brute force attack](/docs/v2/use-cases/brute-force-attacks.md). However, credential stuffing specifically relies on the use of stolen credentials, rather than trying every possible combination of characters. The first line of defense against credential stuffing is to encourage users to use unique, strong passwords for each of their accounts. However, even with strong password policies in place, attackers can still succeed if users reuse passwords or if they have access to a large number of stolen credentials. Friendly Captcha can help protect against credential stuffing attacks by adding an additional layer of security to your login forms. Friendly Captcha looks at various signals from the user's browsing session to determine the likelihood of a request being malicious. If it detects suspicious activity, the user has to solve a computationally expensive challenge which makes the attack more costly and time-consuming for the attacker, while still allowing legitimate users to access their accounts without friction. ## Getting started[​](#getting-started "Direct link to Getting started") To protect your login forms against credential stuffing attacks, you can integrate Friendly Captcha into your authentication flow. This involves adding the Friendly Captcha widget to your login form and configuring it to work with your backend. You can follow our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide to set this up. --- # Fake Accounts Fake accounts are a common problem on platforms, and they can be used for a variety of malicious purposes, such as spreading misinformation, harassing other users, engaging in fraudulent activities, or abusing your service otherwise. These accounts are often created using automated bots, which can quickly generate large numbers of fake accounts to overwhelm a platform. Friendly Captcha helps protect your platform from fake accounts by adding a layer of security that can distinguish between legitimate users and automated bots. This makes it more difficult and costly for attackers to create fake accounts, while allowing legitimate users to access your services without friction. ## How can Friendly Captcha protect against fake accounts?[​](#how-can-friendly-captcha-protect-against-fake-accounts "Direct link to How can Friendly Captcha protect against fake accounts?") When a user attempts to create an account on your platform, you can integrate the Friendly Captcha widget into your registration form. When the user interacts with the form, Friendly Captcha assesses the visitor and gathers signals from their browsing session. If it detects suspicious activity that is indicative of a bot, it will require the user's device to solve a computationally expensive challenge. This adds a significant cost to carrying out fake account creation attacks, as automated bots will struggle to solve the challenges at scale. Meanwhile, legitimate users will typically be able to solve the challenges quickly, allowing them to access your services without unnecessary friction. ## What about human attackers?[​](#what-about-human-attackers "Direct link to What about human attackers?") While Friendly Captcha is effective at blocking automated bots, it may not be able to prevent fake accounts that are created by human attackers. While these users may be slowed down by the challenge, they can still potentially create fake accounts. To prevent such fake accounts, you can use our [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) product, which provides you with risk scores and signals about the browsing session. This can help you identify potentially malicious users and take additional actions against them, such as flagging their accounts for review or requiring additional verification for their accounts. You can enrich the account creation process with the signals provided by Risk Intelligence, such as the user's IP address, device information, and behavior patterns, to make informed decisions about whether to allow account creation, implement additional security checks, and monitor suspicious accounts. ## Getting started[​](#getting-started "Direct link to Getting started") To get started with protecting your platform from fake accounts using Friendly Captcha, you can follow our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide to install the widget on your signup form. --- # Payment Fraud Payment fraud is a significant concern for businesses and consumers alike. It involves unauthorized transactions, identity theft, and various schemes that can lead to financial losses. To combat payment fraud, organizations can implement several strategies: * Implementing strong authentication methods, such as multi-factor authentication (MFA), to verify the identity of users before allowing transactions. * Using machine learning algorithms to analyze transaction patterns and identify potentially fraudulent activities. * Implementing real-time monitoring and alerts to quickly detect and respond to suspicious transactions. Friendly Captcha can help protect against payment fraud in two ways: 1. The Friendly Captcha widget can be integrated into your payment forms to add an additional layer of security. When a user interacts with the form, Friendly Captcha assesses the visitor and gathers signals from their browsing session. If it detects suspicious activity that is indicative of a bot, it will require the user's device to solve a computationally expensive challenge. This makes it more difficult and costly for attackers to carry out automated attacks on your payment forms, while still allowing legitimate users to access your services without friction. 2. You can use our [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) product to feed our risk scores and signals into your existing fraud detection systems. This can help you identify potentially fraudulent transactions and take additional actions against them, such as flagging them for review or requiring additional verification for those transactions. ## Getting started[​](#getting-started "Direct link to Getting started") To get started with protecting your payment forms from fraud using Friendly Captcha, you can follow our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide to install the widget on your payment forms. Additionally, you can explore our [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) product to enhance your ability to identify and mitigate fraudulent transactions. --- # Spam Protection Spam is a common problem for websites and apps that allow user-generated content or have a public-facing form. Spammers can use automated bots to flood your site with unwanted content, which can degrade the user experience, damage your reputation, and even lead to security issues. Friendly Captcha helps protect your site from spam by adding a layer of security that can distinguish between legitimate users and malicious (automated) actors. This makes it more difficult and costly for spammers to abuse your site, while allowing legitimate users to access your services without friction. This challenge is a Proof of Work (PoW) cryptographic puzzle that happens entirely in the background. For an attacker, this challenge will be much more expensive to solve than it is for a legitimate user. ## How does it work?[​](#how-does-it-work "Direct link to How does it work?") You [install the Friendly Captcha widget on the forms you want to protect](https://developer.friendlycaptcha.com/docs/v2/getting-started). When a user or bot interacts with the form, Friendly Captcha makes an assessment of the visitor, and depending on the signals it gathers, it may require the user's device to solve a computationally expensive challenge. This adds a significant cost to carrying out spam attacks, as automated bots will struggle to solve the challenges at scale. Meanwhile, legitimate users will typically be able to solve the challenges quickly, allowing them to access your services without unnecessary friction. ## What about human spammers?[​](#what-about-human-spammers "Direct link to What about human spammers?") While Friendly Captcha is effective at blocking automated spam bots, it may not be able to prevent spam that is generated by human spammers. While these users may be slowed down by the challenge, they can still potentially submit spam content. To mitigate this, you can implement additional measures such as content moderation, user reporting, or manual review processes to identify and remove spam content that may slip through the automated defenses. [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) helps with this by providing you with risk scores and signals about the browsing session, which can help you identify potentially malicious users. You can use this information to prioritize content for review or to take additional actions against users who are suspected of spamming. For example, if you are running a forum that is being targeted by spammers, you could use Risk Intelligence to identify users who are posting spam content and take actions such as temporarily banning them, requiring additional verification for their posts, or flagging their content for review. ## Getting started[​](#getting-started "Direct link to Getting started") To get started with protecting your site from spam using Friendly Captcha, you can follow our [Getting Started](https://developer.friendlycaptcha.com/docs/v2/getting-started/) guide to install the widget on your forms. Additionally, you can explore our [Risk Intelligence](https://developer.friendlycaptcha.com/docs/v2/risk-intelligence/) product to enhance your ability to identify and mitigate spam generated by human spammers. --- # v1 and v2 There are two versions of the Friendly Captcha service, called **v1** and **v2**. The second version contains technical and non-technical enhancements that come from state-of-the-art improvements and learnings from **v1**. ## What's new in v2?[​](#whats-new-in-v2 "Direct link to What's new in v2?") * v2 has **improved protection**. It provides us with more powerful signals to detect abuse, automated browsers, and browsers that have otherwise been tampered with. * v2 improves on **user experience**. Real users are less likely to need to wait for the captcha to finish. * v2 is **easier to integrate**. It has SDKs for many popular programming languages, automatically matches the language of your website, and has simplified Content Security Policy (CSP) requirements. ## What hasn't changed?[​](#what-hasnt-changed "Direct link to What hasn't changed?") * Friendly Captcha protects your websites and apps from bots and abuse. * There are no user tasks like clicking cars, and there are no cookies or user tracking involved. * It supports the same old browsers like Internet Explorer 11. * It offers an EU-only infrastructure option, where we guarantee your users' data does not touch any US-owned server. * The code that runs in your website is [open source](https://github.com/FriendlyCaptcha/friendly-captcha-sdk), so you can be certain you will not be compromised even if we were to get compromised. ## What's going to happen to v1?[​](#whats-going-to-happen-to-v1 "Direct link to What's going to happen to v1?") v1 will keep working! We will maintain it moving forward for multiple years. At some point down the road we will not allow newly created apps to use v1, but existing apps will still continue to work. ## How do I upgrade?[​](#how-do-i-upgrade "Direct link to How do I upgrade?") v2 is available to all customers and we highly recommend it. Please [follow the upgrade guide](/docs/v2/guides/upgrading-from-v1/.md). If you're an **Enterprise** customer, please [contact support](https://friendlycaptcha.com/support/) so that we can help you upgrade to v2! ## How can I tell if I'm using v1 or v2?[​](#how-can-i-tell-if-im-using-v1-or-v2 "Direct link to How can I tell if I'm using v1 or v2?") There are a couple of features that you can use to identify which version you're using. Firstly, the widget that goes on your website or app looks different. **v1 Widget**![](/img/widget-v1-ready.png)![](/img/widget-v1-completed.png) **v2 Widget**![](/img/widget-v2-ready.png)![](/img/widget-v2-completed.png) Secondly, the URLs that you use are different between v1 and v2. **v1 URLs** * Widget SDK script: `https://cdn.jsdelivr.net/npm/friendly-challenge@X.Y.Z/widget.module.min.js`. * NPM package: [`friendly-challenge`](https://www.npmjs.com/package/friendly-challenge). * Verification API endpoint: `https://api.friendlycaptcha.com/api/v1/siteverify`. **v2 URLs** * Widget SDK script: `https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@X.Y.Z/site.min.js`. * NPM package: [`@friendlycaptcha/sdk`](https://www.npmjs.com/package/@friendlycaptcha/sdk). * Verification API endpoint: `https://global.frcapi.com/api/v2/captcha/siteverify` ---