Protect the registration endpoint

Integrating Castle in an account registration workflow


You will need a Castle account and an instance of the Castle SDK configured with your Castle API Secret for the applicable environment.

You can sign up for a free Castle account at


  1. Step 1. Fingerprinting
  2. Step 2. Intercept the registration form
  3. Step 3. Handle the registration request
  4. Step 4. Test

Supported events

  • $registration.attempted


With just a few lines of code, you’ll be denying abusive requests before they reach your application business logic. This helps you eliminate the need for traditional anti-bot measures such as CAPTCHAs.

The scope of this tutorial is blocking bots from completing automated registrations. Castle’s Policies can be customized to create solutions that are outside the scope of this tutorial - for more information about Policies, refer to using policies.


Step 1. Fingerprinting

For optimal performance, Castle fingerprinting must be installed and configured for the mobile and web applications that access the registration endpoint. However, the implementation of Castle fingerprinting is not a technical blocker to continue with the next steps. If you plan to return to install Castle fingerprinting at a later time, you may skip to step 3.

Please refer to our tutorials linked here for fingerprinting instructions:

Step 2. Intercept the registration form

As mentioned in Step 1, if you have not yet implemented Castle fingerprinting, skip to step 3 and return to complete this step once Castle fingerprinting has been implemented.

Mobile apps

After the registration request object has been initialized in your mobile app code, but prior to sending the request, call Castle.clientId(). Append a header to the registration request, using the configuration.CastleClientIdHeaderName as the header name and the Castle.clientId() result as the header value. See the Fingerprinting tutorial(s) relevant to your particular apps for more details.

Web apps

When the user submits the registration form, intercept the form submission and call _castle('getClientId') to generate a single-use token from the Castle.js script running on your web app page. Pass this token as a hidden form-field to your server-side registration handler.

var form = document.getElementById('registration-form');

form.addEventListener("submit", function(evt) {

  // Get the ClientID token
  var clientId = _castle('getClientId');

  // Populate a hidden <input> field named `castle_client_id`
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'castle_client_id');
  hiddenInput.setAttribute('value', clientId);

  // Add the `castle_client_id` to the HTML form


Step 3. Handle the registration request

In your server-side registration handler, extract the clientId value. In the examples from step 2, the clientId will be in a custom header (Castle mobile SDKs default to X-Castle-Client-Id) or in a form field (castle_client_id).

Add any additional context information to the Castle API payload. The user identifier submitted on the client side (email or phone) is a required field and is used by Castle to do the trust assessment.

The deny action is determined by the policies that you have configured in your policy settings. All Castle accounts are created with default policies, including one that will issue a deny for high risk scores seen in $registration.attempted events.

A screenshot of the Castle Dashboard Policy Settings page
Castle default policies include one for $registration.attempted

Step 4. Test

The events debugger will come in handy when inspecting Castle API calls to ensure you got all the details right.

Make sure the client device context details, such as IP and User-Agent, are correct. Do not send the IP and User-Agent of your Load Balancer.

Check that the human user happy-path is unaffected

The first testing you should do is ensure that your “human” user happy-path is unaffected. Since the bot policy should only issue a deny verdict when there is high bot risk, it should not be triggered when you (or any other human users) try to register an account. Perform some happy-path testing on your registration endpoint by yourself or by other human users to verify this.

Imitate a bot

You can send a test $registration.attempted event from a “Bot” (Headless Chrome) using the following shell commands:

  1. Assign your Castle environment API Secret to a variable (below we use SECRET). The API Secret can be found in your Castle Dashboard Settings.
export SECRET=abc123...
  1. Send an event with an obvious Bot User-Agent (such as a headless Chrome version, used below).
curl \
  -X POST \
  -u ":$SECRET" \
  -H "Content-Type: application/json" \
  -d '
    "event": "$registration.attempted",
    "user_traits": {
      "email": ""
    "context": {
      "client_id": false,
      "ip": "100.101.'$RANDOCTET1'.'$RANDOCTET2'",
      "headers": {
        "Accept": "application/*",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/74.0.3729.157 Safari/537.36",
        "Host": ""
  }' | json_pp

Castle’s response for these events will include the recommended action: deny. Your application can use this recommended action, alongside the risk score, to block these registrations from proceeding or to severely limit the registered account functionality.