# Lưu hồ sơ người dùng

> Tạo hoặc cập nhật một hồ sơ người dùng trong Adapty Mail. Hồ sơ người dùng mang theo email và các thuộc tính
> mà Adapty Mail sử dụng để xác định người nhận và xây dựng [phân khúc](/docs/mail-segments).
>
> Xác định từng người dùng bằng một `external_profile_id` ổn định. Gửi cùng một `external_profile_id`
> lần nữa sẽ cập nhật hồ sơ hiện có thay vì tạo bản trùng lặp.

## OpenAPI

```yaml
/api-specs/adapty-mail-api.yaml post /api/v1/profile/save/
openapi: 3.1.0
info:
  title: Adapty Mail API
  version: 1.0.0
  description: |
    Adapty Mail API cho phép bạn gửi hồ sơ người dùng và các sự kiện giao dịch đến Adapty Mail trực tiếp
    từ máy chủ của bạn, mà không cần định tuyến dữ liệu qua SDK.

    Sử dụng để:

    - Thêm người đăng ký khi bạn chưa có cơ sở dữ liệu trong Adapty Mail.
    - Tái sử dụng cơ sở người đăng ký từ các ứng dụng khác của bạn.
    - Cung cấp dữ liệu cho Adapty Mail theo hướng server-to-server, với backend của bạn là nguồn dữ liệu chính.

    Một hồ sơ người dùng có email là đủ cho flow **never purchased**. Mọi flow khác
    (renewal cancelled, billing issue, expired, refunded) đều được điều khiển bởi lịch sử mua hàng, vì vậy những
    hồ sơ đó cũng cần có sự kiện giao dịch để được đặt vào đúng flow.

    Để xem hướng dẫn từng bước, hãy xem [Gửi email và giao dịch qua Adapty Mail API](/docs/mail-send-data-via-api).
servers:
  - url: https://api-mail.adapty.io
    description: Máy chủ production
paths:
  /api/v1/profile/save/:
    post:
      summary: Lưu hồ sơ người dùng
      description: |
        Tạo hoặc cập nhật một hồ sơ người dùng trong Adapty Mail. Hồ sơ người dùng mang theo email và các thuộc tính
        mà Adapty Mail sử dụng để xác định người nhận và xây dựng [phân khúc](/docs/mail-segments).

        Xác định từng người dùng bằng một `external_profile_id` ổn định. Gửi cùng một `external_profile_id`
        lần nữa sẽ cập nhật hồ sơ hiện có thay vì tạo bản trùng lặp.
      operationId: saveProfile
      security:
        - apikeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProfileDTO"
            examples:
              basic:
                summary: Hồ sơ người dùng có email, sẵn sàng cho flow "never purchased"
                value:
                  external_profile_id: user_12345
                  external_created_at: "2026-06-01T10:30:00Z"
                  email: jane@example.com
                  country: US
                  custom_attributes:
                    plan: trial
      responses:
        "200":
          description: Hồ sơ người dùng đã được lưu thành công. Nội dung phản hồi là một đối tượng rỗng.
          content:
            application/json:
              schema:
                type: object
              examples:
                default:
                  value: {}
        "400":
          description: Xác thực thất bại — một trường bắt buộc bị thiếu hoặc không hợp lệ. `field_name` cho biết trường nào.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Errors"
              examples:
                default:
                  value:
                    errors:
                      - message: Field required
                        error_code: base_error
                        status_code: 400
                        field_name: email
        "403":
          description: Thiếu hoặc sai secret API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Errors"
              examples:
                default:
                  value:
                    errors:
                      - message: Secret key doesn't exist
                        error_code: secret_key_does_not_exist_error
                        status_code: 403
                        field_name: null
components:
  schemas:
    ProfileDTO:
      type: object
      required:
        - external_profile_id
        - external_created_at
        - email
      properties:
        external_profile_id:
          type: string
          description: |
            Mã định danh ổn định cho người dùng, do ứng dụng hoặc backend của bạn quản lý. Sử dụng lại cùng một giá trị
            trong các yêu cầu để Adapty Mail liên kết email, lượt nhấp và giao dịch mua với một hồ sơ duy nhất. Không bao giờ sử dụng
            mã định danh ẩn danh hoặc mã định danh theo từng lần cài đặt.
        external_created_at:
          type: string
          format: date-time
          description: |
            Thời gian tạo người dùng, theo định dạng ISO 8601 (ví dụ: `"2026-06-01T10:30:00Z"`).
            Bạn có thể sử dụng ngày này trong các phân khúc.
        email:
          type: string
          format: email
          description: Địa chỉ email của người dùng. Adapty Mail gửi các chiến dịch đến địa chỉ này.
        first_name:
          type: string
          description: Tên của người dùng.
        last_name:
          type: string
          description: Họ của người dùng.
        gender:
          type: string
          description: Giới tính của người dùng.
        birthday:
          type: string
          format: date
          description: "Ngày sinh của người dùng, theo định dạng ISO 8601 (ví dụ: `\"1990-05-21\"`)."
        country:
          type: string
          description: "Quốc gia của người dùng dưới dạng mã ISO 3166-1 alpha-2 viết hoa gồm hai chữ cái (ví dụ: `US`)."
        store_country:
          type: string
          description: "Khu vực cửa hàng của người dùng dưới dạng mã ISO 3166-1 alpha-2 viết hoa gồm hai chữ cái (ví dụ: `US`)."
        custom_attributes:
          type: object
          description: |
            Các cặp key-value tùy ý (giá trị chuỗi hoặc số) để đính kèm vào hồ sơ người dùng. Sử dụng chúng để
            xây dựng [phân khúc](/docs/mail-segments) — ví dụ: `plan`, `signup_source` hoặc `trial_days`.
        device_info:
          $ref: "#/components/schemas/DeviceInfoDTO"
    Errors:
      type: object
      description: Phản hồi lỗi chuẩn. Mọi lỗi đều trả về trạng thái 4XX với cấu trúc này.
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
                description: Mô tả lỗi dễ đọc cho người dùng.
              error_code:
                type: string
                description: Mã định danh lỗi dễ đọc cho máy.
              status_code:
                type: integer
                description: Mã trạng thái HTTP cho lỗi này.
              field_name:
                type: string
                description: Trường trong yêu cầu gây ra lỗi, hoặc `null` nếu lỗi không liên quan đến trường cụ thể nào.
    DeviceInfoDTO:
      type: object
      required:
        - platform
      properties:
        platform:
          type: string
          description: "Nền tảng mà người dùng đang sử dụng, ví dụ: `iOS` hoặc `Android`."
        device:
          type: string
          description: "Kiểu thiết bị, ví dụ: `iPhone15,2`."
        os:
          type: string
          description: "Phiên bản hệ điều hành, ví dụ: `17.5`."
        locale:
          type: string
          description: "Ngôn ngữ và vùng của người dùng, ví dụ: `en-US`."
        timezone:
          type: string
          description: "Múi giờ của người dùng, ví dụ: `America/New_York`."
        app_version:
          type: string
          description: "Phiên bản ứng dụng của bạn mà người dùng đang chạy, ví dụ: `3.1.0`."
  securitySchemes:
    apikeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        Xác thực mọi yêu cầu bằng secret API key của Adapty Mail, được gửi dưới dạng header **Authorization**
        với giá trị `Bearer {your_secret_api_key}`, ví dụ: `Bearer secret_live_...`.

        Tìm key này trong Adapty Mail tại **Settings**. Key này dành riêng cho từng dự án — nó xác định
        dự án mà dữ liệu thuộc về, vì vậy các endpoint hồ sơ và giao dịch không cần nhận project ID.
```
