# Activation API v2


{{< announcement >}}
This feature is in **Beta**, where we work with early users and customers to test new features and get feedback before making them generally available. 

[Contact the Product team](mailto:product@rudderstack.com) if you have any questions.
{{< /announcement >}}

With RudderStack's Activation API, you can fetch enriched user traits stored in your Redis instance and use them for near real-time personalization for your target audience.

You can sync all your customer 360 data from Profiles project to your Redis store. Then, use the Activation API endpoints to retrieve and use the enriched user data for personalization.

{{< image src="images/profiles/activation-api.webp" alt="Activation API" >}}

{{<badge label="API Version" message="2" color="7447fc" >}}

## Prerequisites

{{< customreadfile "/includes/activation-api/prerequisites.md" >}}

## Use Activation API

{{< customreadfile "/includes/activation-api/usage.md" >}}

## Authorization

This API uses [Bearer Authentication](https://swagger.io/docs/specification/authentication/bearer-authentication/) for authenticating all requests. Set the [Service Access Token](#faq) as the bearer token for authentication.

## Base URL

```
https://profiles.rudderstack.com/v2/
```
{{< info >}}
The URL is `https://profiles-eu.rudderstack.com/v2/` if you are hosted in the **EU** region.
{{< /info >}}

## Get user profiles

{{% api-method method="post" host="https://profiles.rudderstack.com/v2" path="/activation" %}}

### Request body

{{< query-paramsList >}}
  {{< query-params keyname="entity" valuename="Entity type" keytype="Required" datatype="String" >}}
  {{< query-params keyname="destinationId" valuename="Redis destination ID." keytype="Required" datatype="String" >}}
  {{< query-params keyname="id" valuename="ID containing `type` and `value`" keytype="Required" datatype="Object" >}}
{{< /query-paramsList >}}

```json
{
  "entity": <entity_type>,  // User, project, account, etc.
  "destinationId": <redis_destination_id> , // Redis destination ID
  "id": {
    "type": <id_type>,
    "value": <id_value>
  }
}
```

### Sample request

{{< tabs tabTotal="3" >}}
{{% tab tabName="HTTP" %}}
```http
POST /v2/activation HTTP/1.1
Host: profiles.rudderstack.com
Content-Type: application/json
Authorization: Bearer <service_access_token>
Content-Length: 90

{
 "entity": <entity_type>,
 "destinationId": <redis_destination_id>, // Redis destination ID
 "id": {
   "type": <id_type>,
   "value": <id_value>
 }
}
```

{{% /tab %}}
{{% tab tabName="CURL" %}}
```bash
curl --location 'https://profiles.rudderstack.com/v2/activation' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <service_access_token>' \
--data '{
 "entity": <entity_type>,
 "destinationId": <redis_destination_id>, // Redis destination ID
 "id": {
   "type": <id_type>,
   "value": <id_value>
 }
}'
```

{{% /tab %}}
{{% tab tabName="Node.js" %}}
```javascript
const axios = require('axios');
let data = JSON.stringify({
  "destinationId": <redis_destination_id>,
  "entity": <entity_type>,
  "id": {
    "type": <id_type>,
    "value": <id_value>
  }
});

let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: 'https://profiles.rudderstack.com/v2/activation',
  headers: {
    'Content-Type': 'application/json',
    'authorization': 'Bearer <service_access_token>'
  },
  data: data
};

axios.request(config)
  .then((response) => {
    console.log(JSON.stringify(response.data));
  })
  .catch((error) => {
    console.log(error);
  });
```

{{% /tab %}}
{{< /tabs >}}

### Responses

{{< tabs tabTotal="4" >}}
{{% tab tabName="PAT absent or unauthorized request" %}}

You will get the following response if the Service Access Token is absent or trying to access a destination to which it does not have access:

```json
statusCode: 401
Response: {
  "error": "Unauthorized request. Please check your access token"
}
```
{{% /tab %}}
{{% tab tabName="Destination ID is absent/blank" %}}

You will get the following response if the destination is not Redis or the destination ID is absent/blank:

```json
statusCode: 404
Response: {
  "error": "Invalid Destination. Please verify you are passing the right destination ID"
}
```
{{% /tab %}}
{{% tab tabName="Destination ID is valid" %}}

You will get the following response if the destination ID is present and valid:

```json
statusCode: 200
Response:
{
  "entity": <entity_type>,
  "id": {
    "type": <id_type>,
    "value": <id_value>
  },
  "data": {
    <traits_from_Redis>
  }
}
```
{{% /tab %}}
{{% tab tabName="Destination ID is absent" %}}

You will get the following response if the destination ID is not present in Redis:

```json
statusCode: 200
Response:
{
  "entity": <entity_type>,
  "id": {
    "type": <id_type>,
    "value": <id_value>
  },
  "data": {}
}
```
{{% /tab %}}
{{< /tabs >}}

## Delete user profiles

{{% api-method method="delete" host="https://profiles.rudderstack.com/v2" path="/activation" %}}

Use this request to delete a single user profile. To delete multiple profiles in one request, see [Batch delete user profiles](#batch-delete-user-profiles).

### Request body

{{< query-paramsList >}}
  {{< query-params keyname="entity" valuename="Entity type" keytype="Required" datatype="String" >}}
  {{< query-params keyname="destinationId" valuename="Redis destination ID." keytype="Required" datatype="String" >}}
  {{< query-params keyname="id" valuename="ID containing `type` and `value`" keytype="Required" datatype="Object" >}}
{{< /query-paramsList >}}

### Sample request

{{< tabs tabTotal="3" >}}
{{% tab tabName="HTTP" %}}
```http
DELETE /v2/activation HTTP/1.1
Host: profiles.rudderstack.com
Content-Type: application/json
Authorization: Bearer <service_access_token>
Content-Length: 90

{
 "entity": <entity_type>,
 "destinationId": <redis_destination_id>, // Redis destination ID
 "id": {
   "type": <id_type>,
   "value": <id_value>
 }
}
```

{{% /tab %}}
{{% tab tabName="CURL" %}}
```bash
curl --location --request DELETE 'https://profiles.rudderstack.com/v2/activation' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <service_access_token>' \
--data '{
 "entity": <entity_type>,
 "destinationId": <redis_destination_id>, // Redis destination ID
 "id": {
   "type": <id_type>,
   "value": <id_value>
 }
}'
```

{{% /tab %}}
{{% tab tabName="Node.js" %}}
```javascript
const axios = require('axios');
let data = JSON.stringify({
  "destinationId": <redis_destination_id>,
  "entity": <entity_type>,
  "id": {
    "type": <id_type>,
    "value": <id_value>
  }
});

let config = {
  method: 'delete',
  maxBodyLength: Infinity,
  url: 'https://profiles.rudderstack.com/v2/activation',
  headers: {
    'Content-Type': 'application/json',
    'authorization': 'Bearer <service_access_token>'
  },
  data: data
};

axios.request(config)
  .then((response) => {
    console.log(JSON.stringify(response.data));
  })
  .catch((error) => {
    console.log(error);
  });
```

{{% /tab %}}
{{< /tabs >}}

### Responses

{{< tabs tabTotal="4" >}}
{{% tab tabName="Success" %}}
```json
statusCode: 200
Response: {
  "deletedKeys": 20,
  "actualKeys": 30
}
```
{{% /tab %}}
{{% tab tabName="Bad request" %}}
```json
statusCode: 400
Response: {
  "message": "id should have at least one item"
}
```
{{% /tab %}}
{{% tab tabName="Not found" %}}
```json
statusCode: 404
Response: {
  "message": "None of the provided userIds were found"
}
```
{{% /tab %}}
{{% tab tabName="Unhandled exceptions" %}}
```json
statusCode: 500
Response: {
  "message": "Internal server error"
}
```
{{% /tab %}}
{{< /tabs >}}

## Batch delete user profiles

{{% api-method method="delete" host="https://profiles.rudderstack.com/v2" path="/activation" %}}

Use this request to delete profiles for multiple identifiers in a single API call. Pass an `ids` array instead of a single `id` object.

### Request body

{{< query-paramsList >}}
  {{< query-params keyname="entity" valuename="Entity type" keytype="Required" datatype="String" >}}
  {{< query-params keyname="destinationId" valuename="Redis destination ID." keytype="Required" datatype="String" >}}
  {{< query-params keyname="ids" valuename="Array of IDs, each containing `type` and `value`" keytype="Required" datatype="Array" >}}
{{< /query-paramsList >}}

```json
{
  "entity": <entity_type>,
  "destinationId": <redis_destination_id>,
  "ids": [
    {
      "type": <id_type>,
      "value": <id_value>
    },
    {
      "type": <id_type>,
      "value": <id_value>
    }
  ]
}
```

### Sample request

{{< tabs tabTotal="3" >}}
{{% tab tabName="HTTP" %}}
```http
DELETE /v2/activation HTTP/1.1
Host: profiles.rudderstack.com
Content-Type: application/json
Authorization: Bearer <service_access_token>
Content-Length: 90

{
  "entity": <entity_type>,
  "destinationId": <redis_destination_id>,
  "ids": [
    {
      "type": <id_type>,
      "value": <id_value>
    },
    {
      "type": <id_type>,
      "value": <id_value>
    }
  ]
}
```

{{% /tab %}}
{{% tab tabName="CURL" %}}
```bash
curl --location --request DELETE 'https://profiles.rudderstack.com/v2/activation' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <service_access_token>' \
--data '{
  "entity": <entity_type>,
  "destinationId": <redis_destination_id>,
  "ids": [
    {
      "type": <id_type>,
      "value": <id_value>
    },
    {
      "type": <id_type>,
      "value": <id_value>
    }
  ]
}'
```

{{% /tab %}}
{{% tab tabName="Node.js" %}}
```javascript
const axios = require('axios');
let data = JSON.stringify({
  "destinationId": <redis_destination_id>,
  "entity": <entity_type>,
  "ids": [
    {
      "type": <id_type>,
      "value": <id_value>
    },
    {
      "type": <id_type>,
      "value": <id_value>
    }
  ]
});

let config = {
  method: 'delete',
  maxBodyLength: Infinity,
  url: 'https://profiles.rudderstack.com/v2/activation',
  headers: {
    'Content-Type': 'application/json',
    'authorization': 'Bearer <service_access_token>'
  },
  data: data
};

axios.request(config)
  .then((response) => {
    console.log(JSON.stringify(response.data));
  })
  .catch((error) => {
    console.log(error);
  });
```

{{% /tab %}}
{{< /tabs >}}

### Responses

{{< tabs tabTotal="4" >}}
{{% tab tabName="Success" %}}
```json
statusCode: 200
Response: {
  "deletedKeys": 1,
  "actualKeys": 2
}
```
{{% /tab %}}
{{% tab tabName="Bad request" %}}
```json
statusCode: 400
Response: {
  "message": "ids should have at least one item"
}
```
{{% /tab %}}
{{% tab tabName="Not found" %}}
```json
statusCode: 404
Response: {
  "message": "None of the provided userIds were found"
}
```
{{% /tab %}}
{{% tab tabName="Unhandled exceptions" %}}
```json
statusCode: 500
Response: {
  "message": "Internal server error"
}
```
{{% /tab %}}
{{< /tabs >}}

## Use case

You can use the Activation API for real-time personalization. Once you fetch the user traits from your Redis instance via the API, you can pull them into your client application to alter the application behavior in real-time based on user interactions.

You can respond immediately with triggered, user-focused messaging based on actions like page views or app clicks and provide a better customer experience.

{{< image src="images/profiles/activation-api-use-case.webp" alt="Real time personalization use case" >}}

## Redis configuration

{{< warning >}}
You must have a working Redis instance in place before setting up the connection.
{{< /warning >}}

- **Address**: Enter the public endpoint of your Redis database. If you are using [Redis Cloud](https://app.redislabs.com/#/), you can find this endpoint by going to your Redis database and navigating to **Configuration** tab > **General**.

{{< image src="images/profiles/redis-public-endpoint.webp" alt="Redis database public endpoint" >}}

- **Password**: Enter the database password. You can find it in the **Security** section of the **Configuration** tab:

{{< image src="images/profiles/redis-database-password.webp" alt="Redis database password" >}}

- **Cluster Mode**: Turn on this setting if you’re connecting to a Redis cluster.
- **Secure**: Enable this setting to secure the TLS communication between RudderStack Redis client and your Redis server.

## Data mapping

RudderStack creates multiple Reverse ETL sources automatically based on your Profiles project. You will see separate sources connected to the same Redis destination.

The following `pb_project.yaml` snippet shows the sources to be created:

```yaml
entities:
  - name: user
    id_types:
      - main_id
      - user_id
      - email
      - salesforce_id
    feature_views:
      using_ids:
        - id: email
          name: features_by_email # Optional. Takes default view name, if not specified.
        - id: salesforce_id
          name: salesforce_id_stitched_features
```

## FAQ

#### How do I generate a workspace-level Service Access Token to use the Activation API?

{{< customreadfile "/includes/activation-api/sat.md" >}}

#### Why am I getting an error trying to enable API in my instance for a custom project hosted on GitHub?

For GitHub projects, you need to explicitly add the IDs of the custom project that need to be served.

In your `pb_project.yaml` file, you can specify them as shown:

```yaml
entities:
  - name: user
    id_types:
      - main_id
      - user_id
      - email
      - salesforce_id
    feature_views:
      name: user_feature_view
      using_ids:
        - id: email
          name: features_by_email
        - id: salesforce_id
          name: salesforce_id_stitched_features
```

#### Does RudderStack perform a full sync if I add a new feature to my project?

Yes, RudderStack updates the mappings and automatically sends all columns from the customer 360 view by triggering a full sync.

#### Suppose I'm running a full sync and the Profiles job is running in parallel and finishes eventually. What happens to the scheduled sync? Does it get queued?

RudderStack first creates a temporary snapshot copy of any sync when it starts. So its syncing the created copy. Even if a Profiles job is running in parallel, the sync - if started - is not impacted by it.

