# User Attribute Upload API

The Ptengine API allows you to bulk upload user attribute data to Ptengine for use in user segmentation analysis and other features.

The API is built on REST architecture, accepts JSON-formatted request bodies, and returns JSON-encoded responses.

***

## 1. Authentication

### Obtaining an API Key

Obtain or create a new API key from the "Integration" page in Ptengine.

{% hint style="warning" %}
API keys are issued at the project level. Each project requires its own API key.
{% endhint %}

### Protecting Your API Key

API keys are confidential. Do not expose them in client-side code such as browsers or mobile apps. Route production requests through your backend server and load keys securely from **environment variables** or a **key management service**.

All API requests must include the API key in the HTTP header:

```
Authorization: Bearer PTENGINE_API_KEY
```

***

## 2. API Specification

| Item        | Details                                     |
| ----------- | ------------------------------------------- |
| API URL     | `https://api.ptengine.com/v1/user/profiles` |
| Protocol    | HTTPS                                       |
| Method      | POST                                        |
| Data Format | JSON                                        |

A single request can upload up to 100 users with their attribute data.

***

## 3. Request Body

The request body contains two fields:

| Field        | Type      | Required | Description                                                                                                          | Limits                                             |
| ------------ | --------- | -------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `field_name` | String\[] | Yes      | Array of attribute key names. Must include `user_id`                                                                 | Maximum 50 fields, each name up to 50 characters   |
| `values`     | Array\[]  | Yes      | Two-dimensional array of user attribute values. The order must correspond to `field_name`. Use `""` for empty values | Maximum 100 users, each value up to 128 characters |

***

## 4. Update Policy

Each request must include `user_id` (a unique user identifier). Ptengine uses `user_id` to check whether the user already exists and processes accordingly:

| User Status    | Attribute Status | Update Policy                           |
| -------------- | ---------------- | --------------------------------------- |
| Exists         | Exists           | Updates the existing attributes         |
| Exists         | Does not exist   | Creates new attributes for the user     |
| Does not exist | /                | Creates a new user and their attributes |

***

## 5. Request Example

Suppose you want to upload the following user attributes:

| user\_id | watch\_conan | watch\_attack\_on\_titan |
| -------- | ------------ | ------------------------ |
| abcd123  | yes          | no                       |
| abcd456  | yes          | yes                      |
| abcd789  | no           | no                       |

The request body would be:

```json
{
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
}
```

{% hint style="info" %}
The order of `values` must match the order of `field_name`.
{% endhint %}

***

## 6. Code Examples

Replace `<API_KEY>` with your actual API key before sending requests.

### Python

```python
import requests
import json

url = "https://api.ptengine.com/v1/user/profiles"

payload = json.dumps({
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
})
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer <API_KEY>'
}

response = requests.post(url, headers=headers, data=payload)
print(response.json())
```

### Node.js

```javascript
const axios = require('axios');
let data = JSON.stringify({
  "field_name": [
    "user_id",
    "watch_conan",
    "watch_attack_on_titan"
  ],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
});

let config = {
  method: 'post',
  url: 'https://api.ptengine.com/v1/user/profiles',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <API_KEY>'
  },
  data: data
};

axios(config)
  .then(response => console.log(response.data))
  .catch(error => console.log(error));
```

### PHP

```php
<?php
$client = new Client();
$headers = [
  'Content-Type' => 'application/json',
  'Authorization' => 'Bearer <API_KEY>'
];
$body = '{
  "field_name": ["user_id", "watch_conan", "watch_attack_on_titan"],
  "values": [
    ["abcd123", "yes", "no"],
    ["abcd456", "yes", "yes"],
    ["abcd789", "no", "no"]
  ]
}';
$request = new Request('POST', 'https://api.ptengine.com/v1/user/profiles', $headers, $body);
$res = $client->sendAsync($request)->wait();
echo $res->getBody();
```

### Java

```java
OkHttpClient client = new OkHttpClient().newBuilder().build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType,
  "{\"field_name\":[\"user_id\",\"watch_conan\",\"watch_attack_on_titan\"],"
  + "\"values\":[[\"abcd123\",\"yes\",\"no\"],"
  + "[\"abcd456\",\"yes\",\"yes\"],"
  + "[\"abcd789\",\"no\",\"no\"]]}");
Request request = new Request.Builder()
  .url("https://api.ptengine.com/v1/user/profiles")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer <API_KEY>")
  .build();
Response response = client.newCall(request).execute();
```

### Go

```go
package main

import (
  "fmt"
  "strings"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://api.ptengine.com/v1/user/profiles"
  payload := strings.NewReader(`{
    "field_name": ["user_id", "watch_conan", "watch_attack_on_titan"],
    "values": [
      ["abcd123", "yes", "no"],
      ["abcd456", "yes", "yes"],
      ["abcd789", "no", "no"]
    ]
  }`)

  req, _ := http.NewRequest("POST", url, payload)
  req.Header.Add("Content-Type", "application/json")
  req.Header.Add("Authorization", "Bearer <API_KEY>")

  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)
  fmt.Println(string(body))
}
```

***

## 7. Response

### Success

HTTP status code `200 OK`

```json
{
  "msg": "Success",
  "timestamp": 1684155980
}
```

### Failure

HTTP status code varies by error type.

```json
{
  "msg": "The reason for the request failure",
  "timestamp": 1684155980
}
```

### Status Code Reference

| HTTP Status        | Description                                                                                                       |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| 200 (OK)           | Request was successful                                                                                            |
| 400 (Bad Request)  | Request could not be accepted. Likely missing required parameters (e.g., `field_name` does not include `user_id`) |
| 401 (Unauthorized) | API key is invalid or incorrect                                                                                   |
| 403 (Forbidden)    | API key does not have the appropriate permissions for this request                                                |
| 404 (Not Found)    | The requested resource does not exist or the URL is incorrect                                                     |
| 500/502/503/504    | An issue occurred on the Ptengine server                                                                          |

***

## 8. Uploading Large Datasets

A single request can upload a maximum of 100 users. If you have more than 100 users, split the data into batches of 100 and send multiple requests.

For example, to upload data for 600 users, split it into 6 requests of 100 users each.


---

# 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://helps.ptengine.com/en/developer/user-data-upload-api.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.
