SDK integration for login

DataDome Account Protect detects account takeover threats and protects you against them

Account Protect can be integrated into your backend through SDK packages that are available on multiple platforms.

📘

Prerequisites for Account Protect

Account Protect is separate from Bot Protect and is not available on your account by default.
Please contact your account manager to enable it.

This service requires a dedicated API key, which will be available on your dashboard once it is enabled.

Main concepts

When a user attempts to log into your website, the Account Protect SDK sends data to DataDome's Account Protect API:

  • When a login is successful, i.e. with valid credentials:
    • The Account Protect API will reply with a recommendation to either allow or deny the login
    • A recommendation means that your application will still make the final decision. Please find below some mitigation examples:
      • Block the login
      • Add the user to a watchlist to monitor their next actions (like purchase or profile updates)
      • Require the user to authenticate with MFA
      • Reset the user password
      • Send the data to enrich your internal fraud tool
  • When a login fails, i.e. with incorrect credentials:
    • The Account Protect API will collect information to enrich our detection models
Overview of the implementation flow for a login attempt

Overview of the implementation flow for a login attempt

Installation

📘

Upgrading an existing integration

The Go, Java, and .NET SDKs are now generated from the Account Protect OpenAPI specification, and their latest major versions contain breaking changes. Follow the migration guides to upgrade your integration.

The Account Protect SDK is distributed on multiple platforms:

You can use one of the commands below to install the relevant package for your application:

npm i @datadome/fraud-sdk-node
dotnet add package DataDome.AspNetCore.Fraud.SDK
<!-- insert in the pom.xml file of the project -->
<dependency>
  <groupId>co.datadome.fraud</groupId>
  <artifactId>fraud-sdk-java</artifactId>
  <version>3.0.0</version>
</dependency>
libraryDependencies += "co.datadome.fraud" % "fraud-sdk-java" % "3.0.0"
pip install datadome-fraud-sdk-python
composer require datadome/fraud-sdk-symfony
# 1. add `datadome/fraud-sdk-laravel` to your project
composer require datadome/fraud-sdk-laravel
# 2. Generate an autoloader
composer dump-autoload
# 3. Edit `config/app.php` to add `DataDomeServiceProvider`
# config/app.php
use DataDome\FraudSdkLaravel\Providers\DataDomeServiceProvider;
[...]
 'providers' => ServiceProvider::defaultProviders()->merge([
 [...]
 DataDomeServiceProvider::class
 
# 4. publish `datadome.php` in the `config` folder
php artisan vendor:publish
gem install datadome_fraud_sdk_ruby
go get github.com/datadome/fraud-sdk-go-package/v2

Usage

Using the SDK requires changes in your application to handle the recommendations provided by DataDome's Account Protect API.

Example for a login event

const { DataDome, LoginEvent, ResponseAction, StatusType } = require("@datadome/fraud-sdk-node");
const express = require("express");
const app = express();

const serverKey = "FRAUD_API_KEY";
const datadomeClient = new DataDome(serverKey);
app.use(express.json());

app.post("/login", async function (req, res) {
  // [...] Do Login
  const accountName = req.body.login;
  const authentication: Authentication = {
    socialProvider: 'google',
    type: 'social',
    mode: 'password',
  };
  const session: Session = { id: 'fake_session_id', createdAt: new Date() };
  const loggedUser = findUser(req.body.login, req.body.password); // Logging in
  const customFields: CustomField[] = [{
    name: "customField",
    value: "customValue",
  }];
  if (!!loggedUser) {
    // Confirm legitimacy of login request
    const user: Pick<User, 'id'> = {
      id: loggedUser.id,
    };
    const ddResponse = await datadomeClient.validate(req, new LoginEvent({
      account: accountName,
      user,
      session,
      status: 'succeeded',
      authentication,
      customFields,
    }));
    if (ddResponse?.action == ResponseAction.ALLOW) {
      // DataDome recommends to allow this login. Continue the authentification process.
      const userIp = ddResponse?.ip;
      const userLocation = ddResponse?.location?.country + ' - ' + ddResponse?.location?.city;
      console.log(`User from $USERLOCATION just logged in with IP $USERIP`);
      // [...]
      res.status(200).send(`Hello ${accountName}`);
    } else {
      // DataDome recommends to deny this login. Enforce "deny" process.
      const denyReasons = ddResponse?.reasons?.join(','); // Retrieving denial reasons
      console.log(`User ${accountName} denied because ${denyReasons}`);
      // [...]
      res.status(401).send('Login Failed');
    }
  } else { // loginFailed
    // Send to DataDome any failed login to enrich our models
    await datadomeClient.collect(req, new LoginEvent({
      account: accountName,
      session,
      status: 'failed',
      authentication,
    }));
    res.status(401).send('Login Failed');
  }
});

app.listen(3000);
using DataDome.AspNetCore.Fraud.SDK;
using DataDome.AspNetCore.Fraud.SDK.Api;
using DataDome.AspNetCore.Fraud.SDK.Model;

var appBuilder = WebApplication.CreateBuilder(args);
appBuilder.Services.AddSingleton(_ => Client.Builder("your-api-key").Build());
var app = appBuilder.Build();

app.MapPost("/login", LoginHandler);

app.Run();

static bool LogUser(string login, string password) => false;

static IResult LoginHandler(HttpContext ctx, Client client)
{
    var login = ctx.Request.Form["login"].ToString();
    var password = ctx.Request.Form["password"].ToString();
    bool isLogged = LogUser(login, password); // Process login

    if (isLogged)
    {
        var user = new LoginPayloadAllOfUser { Id = "fake_user_id" };

        var session = new Session { Id = "fake_session_id", CreatedAt = DateTime.UtcNow };

        var authentication = new LoginPayloadAllOfAuthentication
        {
            Mode = AuthenticationMode.Password,
            SocialProvider = AuthenticationSocialProvider.Google,
            Type = AuthenticationType.Social,
        };

        var builder = new LoginPayload.Builder()
            .Account(login)
            .Status(LoginPayloadStatus.Succeeded)
            .User(user)
            .Authentication(authentication)
            .Session(session);

        var validate = new ValidateLogin(builder).Perform(client, ctx.Request, null);
        if (validate?.Action == ResponseAction.Allow)
        {
            return Results.Ok();
        }
        else
        {
            // Business Logic here
            // MFA
            // Challenge
            // Notification email
            // temporarly lock account
            return Results.Problem("denied by Account Protect API", statusCode: 403);
        }
    }
    else
    {
        var builder = new LoginPayload.Builder()
            .Account(login)
            .Status(LoginPayloadStatus.Failed);
        try
        {
            new CollectLogin(builder).Perform(client, ctx.Request, null);
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine($"error during collection: {ex}");
        }
        return Results.Problem("invalid login", statusCode: 401);
    }
}
package example;

import co.datadome.fraud.Client;
import co.datadome.fraud.ApiException;
import co.datadome.fraud.api.*;
import co.datadome.fraud.model.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.Instant;
import java.util.logging.Logger;

@SpringBootApplication
@RestController
public class ExampleApplication {

    private static final Logger logger = Logger.getLogger(ExampleApplication.class.getName());

    @Bean
    public Client fraudClient() {
        return Client.builder("your-api-key").build();
    }

    private final Client client;

    public ExampleApplication(Client client) {
        this.client = client;
    }

    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }

    private static boolean logUser(String login, String password) {
        return false;
    }

    @PostMapping("/login")
    public ResponseEntity<String> loginHandler(
            @RequestParam String login,
            @RequestParam String password,
            HttpServletRequest request) {

        boolean isLogged = logUser(login, password); // Process login

        if (isLogged) {
            LoginPayloadAllOfUser user = new LoginPayloadAllOfUser();
            user.setId("fake_user_id");

            Session session = new Session();
            session.setId("fake_session_id");
            session.setCreatedAt(Instant.now().toString());

            LoginPayloadAllOfAuthentication authentication = new LoginPayloadAllOfAuthentication();
            authentication.setMode(Authentication.Mode.PASSWORD);
            authentication.setSocialProvider(Authentication.SocialProvider.GOOGLE);
            authentication.setType(Authentication.Type.SOCIAL);

            LoginPayload.Builder builder = LoginPayload.builder()
                    .account(login)
                    .status(LoginPayload.Status.SUCCEEDED)
                    .user(user)
                    .authentication(authentication)
                    .session(session);

            try {
                ResponseLogin validate = new ValidateLogin(builder).perform(client, request, null);
                if (validate.getAction() == ResponseAction.ALLOW) {
                    return ResponseEntity.ok().build();
                } else {
                    // Business Logic here
                    // MFA
                    // Challenge
                    // Notification email
                    // temporarly lock account
                    return ResponseEntity.status(403).body("denied by Account Protect API");
                }
            } catch (ApiException e) {
                logger.severe("error during validation: " + e.getMessage());
                return ResponseEntity.status(403).body("denied by Account Protect API");
            }
        } else {
            LoginPayload.Builder builder = LoginPayload.builder()
                    .account(login)
                    .status(LoginPayload.Status.FAILED);
            try {
                new CollectLogin(builder).perform(client, request, null);
            } catch (ApiException e) {
                logger.severe("error during collection: " + e.getMessage());
            }
            return ResponseEntity.status(401).body("invalid login");
        }
    }
}
// 1. Add these imports
import co.datadome.fraud._
import co.datadome.fraud.api.request.{DataDomeMetadata, LoginEvent}
import co.datadome.fraud.model.{Address, User}

// 2. Declare the constant FraudApiKey with the value provided by DataDome

// 3. Initialization of DataDomeFraudService with the FraudApiKey
val dataDomeFraudService = new DataDomeFraudService(FraudApiKey)

// 4. Example for Finagle - Extend SimpleFilter
// 
class DataDomeLoginFilter[Req, Rep]() extends SimpleFilter[Req, Rep] {
  def apply(request: Req, service: Service[Req, Rep]): Future[Rep] = {
    val req = request.asInstanceOf[http.Request] // Casting to http.Request

    val inputJson: Map[String, String] = upickle.default.read[Map[String, String]](req.contentString)
    val login = inputJson.getOrElse("login", "")

    // 5. Build data and call the service
    val ddm: DataDomeMetadata = requestToDataDomeMetadata(req) // with req coming from your framework
    // see below for an example implementation for Finagle
    
    val response = service(request) // Call authentication service before DataDomeFraudService
    response.onSuccess(response => {
      // Authentication match a valid user
      // Validate using Fraud protection
      val dataDomeResponse = dataDomeFraudService.validate(ddm, new LoginEvent(login))
      /* 
        `validateAsync` and `collectAsync` methods return CompletableFuture<> 
        Note that CompletableFuture can throw Exception and must be handled in your code
      */
      if (dataDomeResponse.isDenied) {
        // Example -  Stop authentication procedure
        Future.exception(new RuntimeException("its a bot"))
      } else {
        // continue
        response
      }
    }).onFailure(t => {
      // Authentication invalid - send data for collection
      dataDomeFraudService.collect(ddm, new LoginEvent(login))
    })
  }
}

// Example implementation to extract the request metadata from a Finagle request
def requestToDataDomeMetadata(req: Request): DataDomeMetadata = {
  val builder = DataDomeMetadata.newBuilder()
    .addr(req.remoteAddress.getHostAddress)
    .method(req.method.name)
    .port(req.remotePort)
    .protocol(req.version.versionString)
    .request(req.uri)

  if (req.accept.nonEmpty) builder.accept(req.accept.mkString(", "))
  
  // retrieve clientId
  req.cookies.get("datadome").foreach(cookie => builder.clientId(cookie.value))

  req.headerMap.get("accept-encoding").foreach(builder.acceptEncoding)
  req.headerMap.get("accept-language").foreach(builder.acceptLanguage)
  req.headerMap.get("connection").foreach(builder.connection)
  req.headerMap.get("from").foreach(builder.from)
  req.headerMap.get("hostname").foreach(builder.serverHostname)
  req.headerMap.get("origin").foreach(builder.origin)
  req.headerMap.get("x-real-ip").foreach(builder.xRealIp)
  req.host.foreach(builder.host)
  req.charset.foreach(builder.acceptCharset)
  req.contentType.foreach(builder.contentType)
  req.referer.foreach(builder.referer)
  req.userAgent.foreach(builder.userAgent)
  req.xForwardedFor.foreach(builder.xForwardedForIp)
  
  builder.build()
}
  
// 4. Instantiate filter
val ddLoginFilter = new DataDomeLoginFilter[Request, Response]()

// 5. - Register filter
val protectedAuthService: Service[http.Request, http.Response] = 
  ddLoginFilter.andThen(loginService)
from datadome_fraud_sdk_python import DataDome, LoginEvent, ResponseAction

datadome_instance = DataDome("FraudAPIKey")

@auth.route('/login', methods=['POST']) 
def login():
    email = request.form.get('email')
    if is_authenticated_user(email):
        dd_response = await datadome_instance.validate(request, LoginEvent(email))
        if(dd_response.action == str(ResponseAction.ALLOW)):
            login_user(email)
        else:
            flash("You are not allowed to log in")
    else:
        await datadome_instance.collect(request, LoginEvent(email))
// 1. Update the .env files with your preferred configuration. 
DATADOME_FRAUD_API_KEY='FRAUD_API_KEY'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'

// 2. Add the required imports in your controller
use DataDome\FraudSdkSymfony\Config\DataDomeOptions;
use DataDome\FraudSdkSymfony\DataDome;
use DataDome\FraudSdkSymfony\Models\Address;
use DataDome\FraudSdkSymfony\Models\LoginEvent;
use DataDome\FraudSdkSymfony\Models\StatusType;
use DataDome\FraudSdkSymfony\Models\RegistrationEvent;
use DataDome\FraudSdkSymfony\Models\Session;
use DataDome\FraudSdkSymfony\Models\User;
use DataDome\FraudSdkSymfony\Models\ResponseAction;

// 3. Invoke the validate and collect methods as required
[...]
if ($this->authenticateUser($userForm)) {
    $loginEvent = new LoginEvent($userForm.login, StatusType::Succeeded);
    $loginResponse = app("DataDome")->validate($request, $loginEvent);

    if ($loginResponse != null && $loginResponse->action == ResponseAction::Allow->jsonSerialize()) {
        // Valid login attempt
        return response()->json([true]);
    } else {
        // Business Logic here
        // MFA
        // Challenge
        // Notification email
        // Temporarily lock account
        return response()->json(["Login denied"]);
    }
}
else {
    $loginEvent = new LoginEvent($userForm.login, StatusType::Failed);
    app("DataDome")->collect($request, $loginEvent);
}

return response()->json([false]);
// 1. Update the .env files with your preferred configuration. 
DATADOME_FRAUD_API_KEY='----'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'

// 2. Add the required imports in your controller
use DataDome\FraudSdkSymfony\Config\DataDomeOptions;
use DataDome\FraudSdkSymfony\DataDome;
use DataDome\FraudSdkSymfony\Models\Address;
use DataDome\FraudSdkSymfony\Models\LoginEvent;
use DataDome\FraudSdkSymfony\Models\StatusType;
use DataDome\FraudSdkSymfony\Models\RegistrationEvent;
use DataDome\FraudSdkSymfony\Models\Session;
use DataDome\FraudSdkSymfony\Models\User;
use DataDome\FraudSdkSymfony\Models\ResponseAction;

// 3. Create a private DataDome object
$key = $_ENV['DATADOME_FRAUD_API_KEY'];
$timeout = $_ENV['DATADOME_TIMEOUT'];
$endpoint = $_ENV['DATADOME_ENDPOINT'];

$options = new DataDomeOptions($key, $timeout, $endpoint);
$this->dataDome = new DataDome($options);

// 4. Invoke the validate and collect methods as required
if ($this->validateLogin("account_guid_to_check")) {
    $loginEvent = new LoginEvent("account_guid_to_check", StatusType::Succeeded);
    $loginResponse = $this->dataDome->validate($request, $loginEvent);

    if ($loginResponse != null && $loginResponse->action == ResponseAction::Allow->jsonSerialize()) {
        // Valid login attempt
        return new JsonResponse([true]);
    } else {
        // Business Logic here
        // MFA
        // Challenge
        // Notification email
        // Temporarily lock account
        return new JsonResponse(["Login denied"]);
    }
}
else {
    $loginEvent = new LoginEvent("account_guid_to_check", StatusType::Failed);
    $this->dataDome->collect($request, $loginEvent);
}
# 1.  Set the value of the DATADOME_FRAUD_API_KEY as an environment variable.
export DATADOME_FRAUD_API_KEY='FRAUD_API_KEY'

# 2. Add the required import in your controller.
require 'datadome_fraud_sdk_ruby'

# 3. Create a DataDome instance.
datadome = DataDome.new

# 4a. If you have access to the http request as `request`:
# Invoke the validate and collect methods.
if login.success?
	login_event = DataDomeLoginEvent.new(account: params[:user][:email], status: DataDomeStatusType::SUCCEEDED)
	datadome_response = datadome.validate(request: request, event: login_event)
  if datadome_response.action == DataDomeResponseAction::ALLOW
    # Implement logic to allow login as usual
  else
    # Business Logic here
    # MFA
    # Challenge
    # Notification email
    # temporarly lock account
  end
else
  login_event = DataDomeLoginEvent.new(account: params[:user][:email], status: DataDomeStatusType::FAILED)
	datadome.collect(request: request, event: login_event)
  
# 4b. If you can't pass the current HTTP request to our SDK:
# i. Create DataDomeHeaders and DataDomeRequest with request information
datadome_headers = DataDomeHeaders.new(addr: "1.1.1.1", client_ip: "1.1.1.1", content_type: "text", host: "https://example.com", port: 80, x_real_ip: "1.1.1.1", x_forwarded_for_ip: "1.1.1.1", accept_encoding: "gzip, deflate, br", accept_language: "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4", accept: "*/*", method: "POST", protocol: "https", server_hostname: "example.com", referer: "https://example.com", user_agent:"curl", from: "[email protected]", request:"/login", origin: "https://example.com", accept_charset: "utf-8, iso-8859-1;q=0.7", connection: "keep-alive", client_id: "")
datadome_request = DataDomeRequest.new(datadome_headers) 
## ii. Use the code from 4a and replace the request with datadome_request.
datadome_response = datadome.validate(request: datadome_request, event: login_event)
datadome_response = datadome.collect(request: datadome_request, event: login_event)
package main

import (
  "log"
  "net/http"
  "time"

  dd "github.com/datadome/fraud-sdk-go-package/v2"
)

func addOpt[T any](opts []T, opt T, err error) []T {
    if err != nil {
        log.Printf("option error: %v", err)
        return opts
    }
    return append(opts, opt)
}

func loginHandler(client *dd.Client) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if r.Method == http.MethodPost {
            _ = r.ParseForm()
            login := r.FormValue("login")
            password := r.FormValue("password")
            isLogged := logUser(login, password) // Process login
            if isLogged {
                userId := "fake_user_id"
                user := dd.LoginPayloadAllOfUser{
                    Id: &userId,
                }

                sessionId := "fake_session_id"
                createdAt := time.Now().Format(time.RFC3339)
                session := dd.Session{
                    Id:        &sessionId,
                    CreatedAt: &createdAt,
                }

                authentication := dd.LoginPayloadAllOfAuthentication{
                    Mode:           dd.AuthenticationModePassword,
                    SocialProvider: dd.AuthenticationSocialProviderGoogle,
                    Type:           dd.AuthenticationTypeSocial,
                }

                var opts []dd.LoginPayloadOption
                userOpt, err := dd.LoginPayloadWithUser(user)
                opts = addOpt(opts, userOpt, err)
                authOpt, err := dd.LoginPayloadWithAuthentication(authentication)
                opts = addOpt(opts, authOpt, err)
                sessionOpt, err := dd.LoginPayloadWithSession(session)
                opts = addOpt(opts, sessionOpt, err)

                op, err := dd.NewValidateLogin(login, dd.LoginPayloadStatusSucceeded, opts...)
                if err != nil {
                    log.Printf("error creating validate login operation: %v\n", err)
                    http.Error(w, "invalid request", http.StatusBadRequest)
                    return
                }
                validate, err := op.PerformOperation(r.Context(), client, r, nil)
                if err != nil {
                    log.Printf("error during validation: %v\n", err)
                }
                if validate.Action == dd.ALLOW {
                    w.WriteHeader(http.StatusOK)
                    return
                } else {
                    // Business Logic here
                    // MFA
                    // Challenge
                    // Notification email
                    // temporarly lock account
                    http.Error(w, "denied by Account Protect API", http.StatusForbidden)
                    return
                }
            } else {
                op, err := dd.NewCollectLogin(login, dd.LoginPayloadStatusFailed)
                if err != nil {
                    log.Printf("error creating collect login operation: %v\n", err)
                } else if err := op.PerformOperation(r.Context(), client, r, nil); err != nil {
                    log.Printf("error during collection: %v\n", err)
                }
                http.Error(w, "invalid login", http.StatusUnauthorized)
                return
            }
        }
    }
}

func main() {
  client, _ := dd.NewClient("FRAUD_API_KEY")
  
  mux := http.NewServeMux()
  mux.HandleFunc("/login", loginHandler(client))
  
  _ = http.ListenAndServe(":8080", mux)
}

API reference

LoginEvent

The SDK exposes methods for login validation that require a LoginEvent instance to be sent to the Account protect API along with the client request itself.

Available properties for this event type are listed below:

NameDescriptionDefault valuePossible valuesOptional
accountThe unique account identifier used for the login attempt.Any string value.
authentication.modeAuthentication modebiometric, mail mfa, otp, password, otherYes
authentication.socialProviderAuthentication social provideramazon, apple, facebook, github, google, linkedin, microsoft, twitter, yahoo, otherYes
authentication.typeAuthentication typelocal, socialProvider, otherYes
accountTypeDescribe the type of the account.guest, staff,external,partner, customer, merchant, vip, test, otherYes
accountCreationDateDate when account was created.Format ISO 8601 YYYY-MM-DDThh:mm:ssTZDYes
customFieldsSee dedicated custom fields section in the FAQ
failReasonReason why the login failedunknownAccount,
wrongPassword ,
expiredPassword,
disabledAccount ,
blockedAccount ,
invalidMfa,
internalBusinessRule,
technicalIssue,
other
partnerIdIdentify the partner using the solution.Any string value.Yes
statusThe status of the login attempt.StatusType.SUCCEEDEDStatusType.SUCCEEDED, StatusType.FAILEDYes
session.createdAtCreation date of the sessionFormat ISO 8601 YYYY-MM-DDThh:mm:ssTZDYes
session.idA unique session identifier from your systemAny string value.Yes
user.idA unique customer identifier from your system. It has to be the same for all other event sentAny string value.No

Validation response

Validating a login event should result in a response that can include the following properties:

NameDescriptionPossible valuesAlways defined
actionThe recommended action to perform on the login attempt.allow, deny, challenge, reviewYes
errorsA list of objects representing each error with details.
Each object will have the properties listed below.
errors[i].errorA short description of the error.
errors[i].fieldThe name of the value that triggered the error.
eventIdEvent identifier associated to this validate event.A valid UUID.Yes
ipThe IP address detected as the origin of the client request.
locationAn object representing the location of a user based on their IP address.
It will have the properties listed below.
location.cityThe complete city name.
location.countryThe complete country name.
location.countryCodeThe country code, using the ISO-3166-1-alpha-2 standard format.
messageA description of the error if the status is failure or timeout.Invalid header / Request timed out...
reasonsA list of reasons to support the recommended action.brute_force, teleportation
scoreThe level of confidence when identifying a request as coming from a fraudster.
Only available in Ruby SDK 2.1.0+, Go SDK v1.1.0+, and Node.Js SDK 2.0.0+, Java SDK 2.3.0+
Integer
statusThe status of the request to the Account Protect API.ok, failure, timeoutYes

Options

Options can be applied to the SDK during its instantiation.

Option NameDescriptionDefault Value
endpointThe endpoint to call for the Account Protect API.https://account-api.datadome.co
timeoutA timeout threshold in milliseconds.
When an API request times out, the SDK will allow it by default.
1500

You can find usage examples for each platform below:

const instance = new DataDome(apiKey, {
    timeout: 1500, 
    endpoint: 'https://account-api.datadome.co',
});
// appsettings.json

// The API key is always required, but can also be passed as
// an environment variable named DataDome__FraudAPIKey
"DataDome": {
    "FraudAPIKey": "----",
    "Timeout": 1500,
    "Endpoint": "https://account-api.datadome.co"
}
new DataDomeFraudService(datadomeFraudApiKey, 
                         DataDomeOptions.newBuilder()
                         .endpoint("https://account-api.datadome.co")
                         .timeout(1500)
                         .build()
  );
datadome_instance =  DataDome("FraudAPIKey", timeout=1500, endpoint="https://account-api.datadome.co")
val dataDomeFraudService = new DataDomeFraudService(datadomeFraudApiKey, 
                                                    DataDomeOptions.newBuilder()
                                                    .endpoint("https://account-api.datadome.co")
                                                    .timeout(1500)
                                                    .build()
                                                   )
// .env

DATADOME_FRAUD_API_KEY='----'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'
// .env

DATADOME_FRAUD_API_KEY='----'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'
datadome = DataDome.new(1500, 'https://account-api.datadome.co', config.logger)
client, err := dd.NewClient(
  "FRAUD_API_KEY",
  dd.ClientWithEndpoint("account-api.datadome.co"),
  dd.ClientWithTimeout(1500),
)

FAQ

What happens if there is a timeout on API request?

The SDK has been designed to have minimal impact on the user experience. If the configured timeout is reached, the SDK will cancel its pending operation and allow the application to proceed.

What happens if the API returns an error?

Errors and timeouts are handled the same way by the SDK: it will not interrupt the application and allow it to proceed.

What happens if my API key is incorrect?

Invalid keys are detected when calling the account protect API. The SDK will return an allow response to avoid blocking any login or registration attempt on the application. This response will also have a failure status and a message that describes the problem.

What are Custom Fields

Custom fields allow to send additional data. Up to 10 custom fields can be defined.

Each field is defined by the following

nametypedescriptionRequired
namestringname of the custom fieldYes
valuestringYes
typestringvalues: Number, String, Phone, email, userId, IPNo
isPiibooleantrueif value contains Personally Identifiable InformationNo

Data type Phone must respect the E.164 format


Did this page help you?