# 用户属性上传 API

通过 Ptengine API，您可以将用户属性信息批量上传到 Ptengine，用于后续的用户分群分析等功能。

本 API 基于 REST 架构构建，接受 JSON 格式的请求体，并返回 JSON 编码的响应。

***

## 1. 认证

### 获取 API Key

在 Ptengine 的「Integration（集成）」页面获取或新建 API Key。

{% hint style="warning" %}
API Key 按项目发放，每个项目需要单独创建。
{% endhint %}

### 保护 API Key

API Key 是机密信息，请勿在浏览器或移动应用等客户端代码中暴露。建议通过后端服务器发送请求，从**环境变量**或**密钥管理服务**中安全读取 Key。

所有 API 请求都需要在 HTTP 请求头中包含 API Key：

```
Authorization: Bearer PTENGINE_API_KEY
```

***

## 2. API 规格

| 项目      | 内容                                          |
| ------- | ------------------------------------------- |
| API URL | `https://api.ptengine.com/v1/user/profiles` |
| 协议      | HTTPS                                       |
| 请求方法    | POST                                        |
| 数据格式    | JSON                                        |

单次请求最多可同时上传 100 个用户及其属性数据。

***

## 3. 请求体

请求体包含以下两个字段：

| 字段名          | 数据类型      | 是否必填 | 说明                                             | 限制                         |
| ------------ | --------- | ---- | ---------------------------------------------- | -------------------------- |
| `field_name` | String\[] | 是    | 用户属性键名的数组，必须包含 `user_id`                       | 最多 50 个字段，每个属性名最长 50 个字符   |
| `values`     | Array\[]  | 是    | 包含用户属性值的二维数组，顺序必须与 `field_name` 对应。空值用 `""` 表示 | 最多 100 个用户，每个属性值最长 128 个字符 |

***

## 4. 更新策略

每个请求必须包含 `user_id`（用户唯一标识符）。Ptengine 通过 `user_id` 判断用户是否已存在，并按以下规则处理：

| 用户状态 | 属性状态 | 更新策略     |
| ---- | ---- | -------- |
| 存在   | 存在   | 更新已有的属性  |
| 存在   | 不存在  | 为该用户新建属性 |
| 不存在  | /    | 新建用户及其属性 |

***

## 5. 请求示例

假设你想上传以下用户属性：

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

请求体如下：

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

{% hint style="info" %}
`values` 的顺序必须与 `field_name` 的顺序一致。
{% endhint %}

***

## 6. 各语言代码示例

发送请求前，请将 `<API_KEY>` 替换为你的实际 API Key。

### 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. 响应

### 成功

HTTP 状态码 `200 OK`

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

### 失败

HTTP 状态码根据错误类型不同而不同。

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

### 状态码参考

| HTTP 状态码           | 说明                                              |
| ------------------ | ----------------------------------------------- |
| 200 (OK)           | 请求成功                                            |
| 400 (Bad Request)  | 请求无法接受。可能缺少必要参数（例如 `field_name` 中未包含 `user_id`） |
| 401 (Unauthorized) | API Key 无效或不正确                                  |
| 403 (Forbidden)    | API Key 没有执行此请求的适当权限                            |
| 404 (Not Found)    | 请求的资源不存在或 URL 不正确                               |
| 500/502/503/504    | Ptengine 服务器发生问题                                |

***

## 8. 大量数据上传

单次请求最多可上传 100 个用户。如果用户数超过 100，请分批发送，每批最多 100 个。

例如，要上传 600 个用户的数据，请拆分为 6 次请求，每次 100 个用户。


---

# 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/cn/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.
