# User Identifier & Properties

The User Identifier is used to uniquely identify a user. For the meaning and importance of User Identifiers and guidelines for choosing one, refer to the [Managing User Identifiers](/en/getting-started/user-identifier.md) document.

#### Using the Device Identifier Managed Internally by the SDK

{% hint style="warning" %}
Available only for Client-side SDKs.
{% endhint %}

Client-side SDKs include functionality for managing the device identifier internally. Users are automatically identified through the internally managed device identifier.

#### Using a Custom Identifier

This method passes a User Identifier directly as a parameter. The identifier you pass can be a Primary Key you manage yourself, a device identifier, a member ID, an email address, a hash value, etc.

* Sample code for creating a custom identifier is provided at the bottom of this document.

***

## Client-side SDK

You can use a User Identifier in two ways.

* Using the device identifier managed internally by the SDK
* Using a custom identifier

#### Using the Device Identifier Managed Internally by the SDK

Client-side SDKs include functionality for managing the device identifier. Therefore, users can be automatically identified without passing a separate User Identifier. For JavaScript, Android, and iOS, you can obtain the device identifier managed by the SDK — refer to the example code below.

```javascript
// Variation distribution
const experimentKey = 42
const variation = hackleClient.variation(experimentKey)

// Event tracking
hackleClient.track("purchase")

// Get the internally managed device identifier
const userId = Hackle.getUserId()
```

```java
// Variation distribution
int experimentKey = 42;
Variation variation = hackleApp.variation(experimentKey);

// Event tracking
hackleApp.track("purchase");

// Get the internally managed device identifier
String deviceId = hackleApp.getDeviceId();
```

```swift
// Variation distribution
let variation = hackleApp.variation(experimentKey: 42)

// Event tracking
hackleApp.track(eventKey: "purchase")

// Get the internally managed device identifier
let deviceId = hackleApp.deviceId
```

```javascript
<HackleProvider hackleClient={hackleClient}>
  <YourApp />
</HackleProvider>
```

#### Using a Custom Identifier

The SDK identifies users via the identifier passed as a parameter. The identifier you pass can be a Primary Key you manage yourself, a device identifier, a member ID, an email address, a hash value, etc.

```javascript
// Variation distribution
const experimentKey = 42;
const user = { userId: "ae2182e0" };
const variation = hackleClient.variation(experimentKey, user);

// Event tracking
hackleClient.track("purchase", user);
```

```java
// Variation distribution
long experimentKey = 42L;
String userId = "ae2182e0";
Variation variation = hackleApp.variation(experimentKey, userId);

// Event tracking
hackleApp.track("purchase", userId);
```

```swift
// Variation distribution
let variation = hackleApp.variation(experimentKey: 42, userId: "ae2182e0")

// Event tracking
hackleApp.track(eventKey: "purchase", userId: "ae2182e0")
```

```javascript
const user = {
    userId: "ae2182e0"
}

<HackleProvider hackleClient={hackleClient} user={user}>
  <YourApp />
</HackleProvider>
```

***

## Server-side SDK

Since the Server-side SDK cannot identify users on its own, you must **always pass a custom identifier as a parameter**.

```java
// Variation distribution
long experimentKey = 42L;
User user = User.builder().userId("ae2182e0");
Variation variation = hackleClient.variation(experimentKey, user);

// Event tracking
hackleClient.track("purchase", user);
```

```python
# Variation distribution
uesr = Hackle.user(user_id='ae2182e0')
variation = hackle_client.variation(experiment_key=42, user=user)

# Event tracking
event = Hackle.event(key='purchase')
hackle_client.track(event=event, user=user)
```

```javascript
// Variation distribution
const experimentKey = 42;
const user = { userId: "ae2182e0" };
hackleClient.variation(experimentKey, user);

// Event tracking
hackleClient.track("purchase", user);
```

```php
// Variation distribution
$experimentKey = 42;
$userId = 'ae2182e0'
$user = \Hackle\Common\HackleUser::builder()->userId($userId)->build();
$variation = $hackleClient->variation($experimentKey, $user);

// Event tracking
$hackleClient->track('purchase', $user);
```

```ruby
# Variation distribution
user = Hackle.user(user_id: 'ae2182e0')
variation = hackle_client.variation(experiment_key: 42, user: user)

# Event tracking
event = Hackle.event(key: 'purchase')
hackle_client.track(event: event, user: user)
```

***

## Example: Creating a Custom Identifier

If you only need data for logged-in users, you can use the member ID or email address used at login as the identifier. However, if you need to include non-logged-in users, it is recommended to use a value that can distinguish users based on the app, device, or browser. (For mobile apps, use a UUID or ADID value; for PC/Mobile web, use a cookie value.)

Below is an example of generating a User Identifier based on cookies.

```javascript
import Cookies from "js-cookie"
import uuid4 from "uuid4"
function getUserId() {
  const key = "PCID" // Enter your preferred name
  const id = Cookies.get(key)
  if (id) {
    return id
  } else {
    const id = uuid4()
    const [top, second] = window.location.hostname.split(".").reverse()
    const domain = `.${second}.${top}`
    Cookies.set(key, id, { expires: 99999, domain: domain, path: "/" })
    return id
  }
}
```

```javascript
const app = require("express")()
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const {v4: uuidv4} = require("uuid")

app.use(bodyParser.urlencoded({extended: false}))
app.use(cookieParser())
app.set("view engine", "ejs")
app.set("views", "views")

function extractDomain(hostname) {
    const DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]+\.[a-z.]{2,6}$/i;
    const SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
    let domain_regex = DOMAIN_MATCH_REGEX;
    const parts = hostname.split(".");
    const tld = parts[parts.length - 1];
    if (tld.length > 4 || tld === "com" || tld === "org") {
        domain_regex = SIMPLE_DOMAIN_MATCH_REGEX;
    }
    const matches = hostname.match(domain_regex);
    return matches ? matches[0] : "";
}

app.use((req, res, next) => {
    const domain = extractDomain(req.headers.host)

    if (!req.cookies.deviceId) {
        const deviceId = uuidv4()
        res.cookie("deviceId", deviceId, {
            maxAge: 365 * 10 * 365 * 24 * 60 * 60,
            domain: domain,
            path: "/"
        })
        req.cookies.deviceId = deviceId
    }
    next()
});

app.get("/", (req, res) => {
    console.log(req.cookies.deviceId)
    res.render("index")
});

app.listen(3000, () => {
    console.log("App Start")
});
```

***

## Matching Identifiers Between Client and Server

When using Client and Server SDKs simultaneously, you can align the identifier for the same user across both environments.

In this case, it is recommended to pass the identifier via an HTTP header during client-server API communication, as shown below.

```http
X-DEVICE-ID: a3017217-3d46-4d7e-8d88-a0a905709fe4
```

## Property

The Hackle SDK supports adding properties to the User object. By sending user-specific information as User Properties, you can reduce repetitive coding while making rich use of them in A/B Tests and data analytics.

Properties must be sent as key-value pairs. A maximum of 64 properties can be added to a user object.

### Property Key

Create keys like regular variable names, but make them easy to identify. The character limit is 128 characters. Case-insensitive. For example, `AGE` and `age` are recognized as the same Property Key.

### Property Value

value supports boolean, string, and number types. For string type, the character limit is 1024 characters. String values are case-sensitive. For example, `APPLE` and `apple` are recognized as different Property Values. For number type, up to 15 integer digits and up to 6 decimal places are supported.

### Example

The User object is used as a parameter in variation distribution, Feature Flag decisions, and event tracking. The example below shows three properties (`age`, `grade`, `is_paying_user`) being added.

```java
import io.hackle.android.HackleApp
import io.hackle.sdk.common.User
import io.hackle.sdk.common.Variation

User user = User.builder()
  	.userId(userId)
    .property("age", 30)
    .property("grade", "GOLD")
    .property("is_paying_user", false)
    .build();

// Variation distribution
Variation variation = hackleApp.variation(experimentKey, user);

// Feature Flag decision
boolean featureOn = hackleApp.isFeatureOn(featureKey, user);

// Event tracking
hackleApp.track(event, user);
```

```kotlin
import io.hackle.android.Hackle
import io.hackle.sdk.common.User
import io.hackle.sdk.common.Variation

val user = Hackle.user() {
  	userId(userId)
    property("age", 30)
    property("grade", "GOLD")
    property("is_paying_user", false)
}

// Variation distribution
val variation = hackleApp.variation(experimentKey, user)

// Feature Flag decision
val featureOn = hackleApp.isFeatureOn(featureKey, user)

// Event tracking
hackleApp.track(event, user)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hackle.io/en/development-guide/sdk/user-identifier.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
