> This page is for Applications Protection.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.humansecurity.com/llms.txt.

# Expo Integration

You can integrate the SDK into an [Expo](https://expo.dev/) project. This article explains how to integrate and implement the HUMAN Expo SDK wrapper into an Expo app.

## Integrate the HUMAN Expo Module

To integrate the HUMAN SDK into your Expo app, follow these steps:

#### Install the package

In your project root, run:

```bash
npm install @humansecurity/expo-mobile-sdk
```

#### Add the plugin

> **Version Compatibility**
>
> The config plugin is available starting from **version 1.0.8 and later**. If you are using an earlier version (1.0.2 – 1.0.6), **skip this step** and continue to [Step 3.](#run-prebuild)

Add the plugin to your Expo config (`app.json` or `app.config.js`) so the Android dependency repository is automatically configured:

```bash
{
  "expo": {
    "plugins": [
      "@humansecurity/expo-mobile-sdk"
    ]
  }
}
```

> **Warning**
>
> Without this step, Android builds will fail to resolve the HUMAN SDK dependency.

#### Run prebuild

Generate the native project files:

```bash
npx expo prebuild
```

> **Note for Custom / Bare Workflows (no expo prebuild)**
>
> If your project **does not use expo prebuild**, you must manually add the **HUMAN Maven repository** so Android can resolve the SDK dependencies.
>
> Add the following line **everywhere you define repositories** (for example, in `settings.gradle`, `build.gradle`, or module-level Gradle files):
>
> ```bash
>     repositories {
>         // ... your existing repos
>         maven { url 'https://jfrog.humansecurity.com/artifactory/human-android-sdk/' }
>     }
> ```
>
> The config plugin automatically adds this repository during npx expo prebuild. In custom or bare workflows, this must be added manually to ensure proper Android dependency resolution.

## HUMAN Expo Mobile API

The HUMAN Security Expo Mobile API offers the following functions:

```typescript
export declare class HumanSecurity {
    static sdkVersion(): string;
    static startWithAppId(appId: string, policy?: HSPolicy): Promise<void>;
    static startWithAppIds(appIds: string[], policy?: HSPolicy): Promise<void>;
    static vid(appId: string): string | null;
    static BD: {
        headersForURLRequest(appId?: string): Promise<{ [key: string]: string }>;
        handleResponse(response: string, code: number, url: string): Promise<HSBotDefenderChallengeResult>;
        canHandleResponse(response: string, code: number, url: string): boolean;
        challengeReferenceId(): string;
        setCustomParameters(parameters: {[key: string]: string}, appId?: string): void;
        addListener(eventName: string, callback: (data: any) => void): import("expo-modules-core").Subscription;
    };
    static AD: {
        setUserId(userId: string | null, appId?: string): void;
        registerOutgoingUrlRequest(url: string, appId?: string): void;
        setAdditionalData(parameters: {[key: string]: string}, appId?: string): void;
    };
}
```

## How to start the SDK

The most important step is to start the SDK as early as possible in your app flow. If your app sends a URL request to your server before the SDK starts, the attached headers won’t include the latest updated tokens. As a result, HUMAN’s Enforcer could block the request, and the SDK won’t be able to present a challenge to the user. Also, be sure to start the SDK only once.

To set it up, call `startWithAppId` in your Expo main entry point (index.tsx) or at the starting point of your app, where you register and initialize one-time services. It’s better to start the SDK outside of any UI component to avoid lifecycle issues.

```typescript
import { HSPolicy, HumanSecurity } from "@humansecurity/expo-mobile-sdk";

// Initialize SDK once on app startup
(() => {
    HumanSecurity.startWithAppId("<APP_ID>")
    .then(() => {
        console.log("SDK started successfully");
    })
    .catch((error) => {
        console.error("Error starting the SDK:", error);
    });
})();
```

> **Warning**
>
> Don't forget to change the `<APP_ID>` to your own appID.

#### Policy options

Currently, the `HSPolicy` class includes two settings:

1. **hybridAppPolicy** — Use this if your app operates in hybrid mode, meaning it has a WebView that loads a site also protected by HUMAN and running its sensor. In this case, configure all relevant website domains with the appID. Set each domain to the bare registrable root (`example.com`). A leading dot is accepted and equivalent; a leading `www.` is stripped to that root. Do not list a sub-domain such as `api.example.com`. Hybrid **is** supported for Expo WebView when `webRootDomains` is set; the Expo module turns `supportExternalWebViews` on. See [WebView container support](/applications/webview-container-support-v5) for more information.
2. **detectionPolicy** — This setting lets you configure whether the SDK can send touch and motion events, either touch only or both touch and motion, to the server to improve detection. By default, it is off, and you can choose to enable it.

For example:

```typescript
import { HSPolicy, HumanSecurity } from "@humansecurity/expo-mobile-sdk";

// Initialize SDK once on app startup
(() => {
    const policy: HSPolicy = {
        hybridAppPolicy: {
             webRootDomains: {
                 APP_ID: ["example.com"],
             },
           },
        detectionPolicy: {
           allowTouchDetection: true,
           allowDeviceMotionDetection: true,
         },
    };

    HumanSecurity.startWithAppId("<APP_ID>", policy)
    .then(() => {
        console.log("SDK started successfully");
    })
    .catch((error) => {
        console.error("Error starting the SDK:", error);
    });
})();
```

Here’s what the code includes:

1. We start the SDK as soon as possible and only once. (You can move this code to a different function/file and export it, as long as it’s called only once.)
2. The `startWithAppId` function of the SDK is called with the following parameters::
   * Your appID.
   * The policy object, if needed.
3. In the second example, an `HSPolicy` instance configures the SDK’s behavior. This object defines settings such as hybridAppPolicy.webRootDomains, indicating that the app uses WebViews containing pages from a domain also protected by HUMAN Security, which is considered hybrid mode. Set `webRootDomains` to the bare registrable root (`example.com`). A leading dot is accepted and equivalent; do not list `www` or a sub-domain. Additionally, detectionPolicy is configured to send touch and motion data. If these parameters aren’t needed, you can simply omit the policy.

> **Note**
>
> If your app communicates with multiple servers that use different appIDs, you can call `HumanSecurity/startWithAppIds(appIds: string[], policy?: HSPolicy): Promise<void>`. This lets you to pass an array of appIDs. In this case, you must specify the relevant appID for each API call to the SDK. If you have only one appID, providing it in most functions is optional.

## Bot Defender Integration

### Add the SDK’s HTTP headers to your URL requests and handle blocked requests

* The SDK provides HTTP headers that your app must add to URL requests. **It’s essential to include these headers in every request**.
* You **shouldn’t** cache these HTTP headers. They contain a token with an expiration date, and the SDK keeps it up to date. Ensure the code isn’t cached so that each request triggers a call to the HumanSecurity module.
* The SDK can handle the blocked request and present a challenge to the user.
* Ensure that arguments are passed correctly to the wrapper. For example, when using `axios`, you might need to call JSON.stringify(error.response?.data) before passing the response body to the SDK. Additionally, in `Axios`, the catch block handles blocked responses because it indicates a failed request.

The following are examples using either a fetch API or using `axios`.

**`Using simple fetch API`**

```typescript title="Using simple fetch API"
import HumanSecurity, { HSBotDefenderChallengeResult } from "@humansecurity/expo-mobile-sdk";


static async makeApiCall(url: string): Promise<void> {
    try {
        const headers = await HumanSecurity.BD.headersForURLRequest(HumanSecurityManager.appId);

        const response = await fetch(url, {
            method: "GET",
            headers,
        });

        const statusCode = response.status;
        if (statusCode !== 200) {
            const responseText = await response.text();
            const canHandle = HumanSecurity.BD.canHandleResponse(responseText, statusCode, url);

            if (canHandle) {
                HumanSecurity.BD.handleResponse(responseText, statusCode, url)
                  .then((result) => {
                    if (result === HSBotDefenderChallengeResult.SOLVED) {
                      console.log("Challenge solved successfully! Retrying request...");
                    } else {
                      console.log("Challenge was cancelled, or failed.");
                    }
                  })
                  .catch((error) => {
                    console.error("Error handling response:", error);
                  });
            } else {
                console.log("Response cannot be handled by the library.");
            }
        } else {
            console.log(`API Call Successful with status ${statusCode}`);
        }
    } catch (error) {
        console.error("Error making API call:", error);
    }
}
```

**`Using axios`**

```typescript title="Using axios"
import { HumanSecurity, HSBotDefenderChallengeResult } from "@humansecurity/expo-mobile-sdk";
import axios from "axios";

static async makeApiCallWithAxios(url: string): Promise<void> {
    try {
        // Get headers from HumanSecurity
        const headers = await HumanSecurity.BD.headersForURLRequest(HumanSecurityManager.appId);
        const response = await axios.get(url, {
            headers,
        });

        // Do whatever you do with the response.
        console.log(`API Call Successful with response ${JSON.stringify(response.status)}`);
    } catch (error: any) {
        console.log("Error making API call:", error);

        // Ensure response exists before accessing status
        const statusCode = error.response?.status || 0;
        const responseText = error.response?.data || "No response data";

        console.log(`API Call failed with status: ${statusCode}`);
        console.log("Response Text: " + JSON.stringify(responseText));

        // Check if HumanSecurity can handle the response
        const canHandle = HumanSecurity.BD.canHandleResponse(JSON.stringify(responseText), statusCode, url)
        if (canHandle) {
            HumanSecurity.BD.handleResponse(
              JSON.stringify(responseText),
              statusCode,
              url
            )
            .then((result) => {
              console.log(`Challenge result: ${HSBotDefenderChallengeResult[result]}`);
            })
            .catch((handleError) => {
              console.error("Error handling response:", handleError);
            });
        } else {
            console.log("Response cannot be handled by the library.");
        }
    }
}
```

Here’s what the code includes:

1. The SDK provides HTTP headers.
2. The HTTP headers are added to the URL requests.
3. The URL request is sent.
4. In case of an error, the response is sent to the SDK, which checks if it’s a blocked request by calling `canHandleResponse`. If `canHandleResponse` returns true, `handleResponse` is called to present the challenge to the user.
5. The challenge result `HSBotDefenderChallengeResult` can be one of the following:
   * **SOLVED** – The user successfully completed the challenge.
   * **CANCELLED** – The user dismissed or failed to complete the challenge.
   * **FAILED** – The SDK was unable to handle the response, usually indicating that something wasn't passed correctly to the `handleResponse` function.

Note that you can await `handleResponse`, but in this case, you would be waiting until the user solves the challenge. You can also continue your flow and handle the result in the `.then` block, as shown in the example.

### What to do when a request is blocked

* **Handle it as a failure.** Your app should treat the blocked request as a failure. However, keep in mind that your app’s UI will be shown again after the user has solved or cancelled the challenge. If the request was triggered by a user action, you should make it clear that the user may try again.
* **Use the promise result** to write analytics, logs, etc.
* You may use the returned promise to **retry the original request** when appropriate. Consider the following:
  * The promise returns out of the original request’s scope. Make sure you handle that case correctly.
  * The user might cancel the challenge. In this case, you shouldn’t retry the request because it would be blocked again.
  * The retry attempt might fail. **Don’t** assume it will be successful.

### Understanding the block response

1. HUMAN's Enforcer, when it decides to block a request, returns a JSON string in the response body with status code 403. For example:

```json
{
  "vid": "928d7ab3-9cf1-11ee-a624-b802520f369f",
  "uuid": "fd01e6d6-9cf2-11ee-808c-acde48001122",
  "page": "...",
  "appId": "PXj9y4Q8Em",
  "action": "captcha",
  "collectorUrl": "https://collector-pxj9y4q8em.perimeterx.net"
}
```

2. The JSON contains metadata for the SDK.
3. Your app should pass the entire JSON as `response` in a **string** format, along with the status code and URL, to the SDK via the following. Otherwise, the SDK won’t present a challenge to the user.

```typescript
HumanSecurity.BD.handleResponse(response: string, code: number, url: string): Promise<HSBotDefenderChallengeResult>
```

### Set Custom Parameters for Bot Defender (Optional)

You can set custom parameters to configure HUMAN's backend with additional values. To do so:

* Set those parameters as an object in JavaScript using keys in the format custom_param\_\[x], where \[x] is a number between 1 and 10.
* After calling `startWithAppId`, call `setCustomParameters`.

For example:

```typescript
import { HumanSecurity } from "@humansecurity/expo-mobile-sdk";

function setCustomParametersForBotDefender() {
    const parameters = { custom_param1: "value1", custom_param2: "value2" };
    try {
        HumanSecurity.BD.setCustomParameters(parameters, "<APP_ID>");
        console.log("Custom Parameters Set Successfully");
    } catch (error) {
        console.log("Failed to Set Custom Parameters");
    }
}
```

### Bot Defender Delegates

Bot Defender provides several delegates, or callbacks, to notify about events related to headers and challenges. These callbacks are mainly intended for analytics and statistics rather than for taking specific actions. In the Expo library, these callbacks work as events.

To listen to a specific event, you must add a listener for it. When the event triggers, you can log or handle the result accordingly. Be sure to remove the listener when leaving the context to avoid memory leaks.

Below is an example implementation for listening to all events and also how to handle a single specific event.

```typescript
import { HumanSecurity, HS_EVENTS } from "@humansecurity/expo-mobile-sdk";
import { useEffect } from "react";

useEffect(() => {
  const eventListeners = Object.values(HS_EVENTS).map((eventName) => {
    console.log(`Registering listener for event: ${eventName}`);
    return HumanSecurity.BD.addListener(eventName, (data) => {
      handleEvent(eventName, data);
    });
  });

  return () => {
    eventListeners.forEach((subscription) => subscription.remove());
  };
}, []);

const handleEvent = (eventName: string, data: any) => {
  console.log(`Event Received: ${eventName}`, data);
  // This is the full list of events.
  switch (eventName) {
    case HS_EVENTS.BOT_DEFENDER_REQUEST_BLOCKED:
      console.log("Request Blocked");
      break;

    case HS_EVENTS.BOT_DEFENDER_CHALLENGE_SOLVED:
      console.log("Challenge Solved");
      break;

    case HS_EVENTS.BOT_DEFENDER_CHALLENGE_CANCELLED:
      console.log("Challenge Cancelled");
      break;

    case HS_EVENTS.BOT_DEFENDER_CHALLENGE_RENDERED:
      console.log("Challenge Rendered");
      break;

    case HS_EVENTS.BOT_DEFENDER_CHALLENGE_RENDER_FAILED:
      console.log("Challenge Render Failed");
      break;

    case HS_EVENTS.BOT_DEFENDER_DID_UPDATE_HEADERS:
      console.log("Headers Were Updated");
      break;

    default:
      console.log("Unknown event received:", eventName);
      break;
  }
};
```

### Listening to a Single Event

If you only want to listen to a single specific event, you can do the following:

```typescript
useEffect(() => {
  const specificEvent = HS_EVENTS.BOT_DEFENDER_CHALLENGE_SOLVED;
  console.log(`Registering listener for event: ${specificEvent}`);

  const eventListener = HumanSecurity.BD.addListener(specificEvent, (data) => {
    // Handle the specific event
    console.log(`Specific Event Received: ${specificEvent}`, data);
  });

  // Clean up the listener when leaving the context
  return () => {
    eventListener.remove();
  };
}, []);
```

## Account Defender Integration

### Enable Account Defender in your app

In order to enable Account Defender, you should set the `UserID` of your current logged-in user in the SDK. When the user logs out, make sure to call the function with null to clear the user ID. For example:

```typescript
import { HumanSecurity } from "@humansecurity/expo-mobile-sdk";

function onUserLoggedIn(userID) {
  try {
    HumanSecurity.AD.setUserId(userID, "<APP_ID>");
  } catch (error) {
    console.log(`Exception: ${error.message}`);
  }
}

function onUserLoggedOut() {
  try {
    HumanSecurity.AD.setUserId(null, "<APP_ID>");
  } catch (error) {
    console.log(`Exception: ${error.message}`);
  }
}
```

### Notify HUMAN's backend on outgoing URL requests from the app

In order to allow Account Defender to protect the user's account, your app must provide the SDK with outgoing URL requests.
This should be done **after** you set the user id.

```typescript
import { HumanSecurity } from "@humansecurity/expo-mobile-sdk";

...
   // In your sendAPIRequest function

    const headers = await HumanSecurity.BD.headersForURLRequest(appId);

    // Add this for account defender
    HumanSecurity.AD.registerOutgoingUrlRequest(url, appId);

    const response = await fetch(url, {
      method: "GET",
      headers,
    });

...
```

Here’s what the code includes:

Call the `HumanSecurity.AD.registerOutgoingUrlRequest(url: string, appId?: string): void` function before sending the URL request. If you're using a custom HttpClient service, add this function to the request interceptor.

### Set additional data (optional)

You can set additional data to configure HUMAN's backend with extra parameters.

To do so, call `HumanSecurity.AD.setAdditionalData(parameters: { [key: string]: string }, appId?: string): void` **after** calling `HumanSecurity.startWithAppId(appId: string, policy?: HSPolicy): Promise<void>`.

For example:

```typescript
import { HumanSecurity } from "@humansecurity/expo-mobile-sdk";

function setAdditionalDataForAccountDefender() {
    try {
        const additionalData = {
          my_key1: "hello",
          my_key2: "world"
      };
      HumanSecurity.AD.setAdditionalData(additionalData, "<APP_ID>");
    } catch (error) {
        console.error(`Failed to Set Additional Data: ${error.message}`);
    }
}
```

## Simulate a challenge

You can simulate a challenge to test the integration. To do so, follow the steps in our [help article.](/applications/ios-how-to-test-you-app-with-the-sdk#how-to-simulate-a-challenge)