> For the complete documentation index, see [llms.txt](https://docs.hackle.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hackle.io/en/user-view/csv-import.md).

# User Upload (CSV Import)

## Introduction

You can upload a CSV file to create or update multiple users along with their `custom properties` and `CRM properties` at once.

Based on `$userId`, existing users are updated and users that do not exist are newly created. (upsert)

This is useful when you want to apply a large volume of users in bulk without individual entry. (Event data is not supported; only user properties are supported.)

## **Steps**

CSV Import consists of the following steps.

### 1. File Upload Step

This is the step where you upload the CSV file.

**The first row of the CSV is the header.**

* Each value in the header is a column key.
* In the CSV example below, the column keys are `$userId`, `$phoneNumber`, `$kakaoMarketingSubscribe`, and `username`.

<figure><img src="/files/zMxj0syRhkfnBePzAsgZ" alt=""><figcaption></figcaption></figure>

You can customize which field each column key maps to in step 2 (the Column Mapping step), so feel free to write the header in a way that is easy for you to recognize.

Alternatively, you can use reserved words so that each column is mapped automatically to its matching field. ([Reserved Field Rules](#reserved-field-rules))

#### Notes

* File upload supports up to **1 million rows**. Files with more than 1 million rows cannot be uploaded.
* File sizes up to **100MB** are supported.
* **All values are stored as strings (String).**
  * To handle dates, we recommend entering them in a form that allows string/number comparison, such as `20260708`.
  * To update CRM properties, you must enter an appropriate value. ([Reserved Field Rules](#reserved-field-rules))
* **Array and object formats are not supported.**
  * A value must always be a single string.
* **All properties are applied as an overwrite ($set).**
  * Empty values are ignored and do not overwrite existing values.
* **The encoding is UTF-8**, and it follows the **RFC 4180 rules** (handling of commas, quotes, and line breaks).

{% hint style="info" %}
You can download the sample template (`$userId, <key1>, <key2> ...`) within the Dashboard and fill it out according to the format.
{% endhint %}

### 2. Column Mapping Step

This is the step where you decide which user field each column of the uploaded CSV maps to.

The fields you can map to are `IDENTIFIER`, `CRM`, and `CUSTOM`.

If a CSV column contains a reserved word, it is mapped to the corresponding reserved field by default; otherwise, it is mapped to the `CUSTOM` field. ([Reserved Field Rules](#reserved-field-rules))

#### Notes

* The `IDENTIFIER` **field must be mapped.**
* **Each** `CRM` **property has a defined set of allowed values, so check (Reserved Field Rules) when writing your CSV file.**

### 3-1. Pre-validation Step

This is the step where you can check for invalid rows based on the uploaded CSV and the column mapping information.

Invalid rows are as follows.

* For a CRM property, a value that does not match the property is entered
* For the $userId property among IDENTIFIER properties, the value consists only of whitespace

### 3-2. Import Start Step

Once everything is ready, you can start the import.

At the moment the import starts, it is only recorded as "Pending"; the actual application is processed by a batch that runs every 10 minutes.

As a result, the changes are not applied immediately after the upload — it takes the wait time (up to 10 minutes) plus the processing time.

## Reserved Field Rules

Reserved words are column names that start with `$`. If you use a reserved word in the CSV header, it is **mapped automatically to the corresponding field in the Column Mapping step**.

All column names that are not reserved words are mapped to the `CUSTOM` field, and the column name becomes the key of the custom property as-is.

#### List of Reserved Words

<table><thead><tr><th>Column Name (Reserved Word)</th><th width="214.4296875">Mapped Field</th><th>Allowed Values</th></tr></thead><tbody><tr><td><code>$userId</code></td><td>IDENTIFIER</td><td>Value that identifies the user (required)</td></tr><tr><td><code>$phoneNumber</code></td><td>CRM</td><td>Mobile phone number</td></tr><tr><td><code>$pushMarketingSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr><tr><td><code>$pushInformationSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr><tr><td><code>$kakaoMarketingSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr><tr><td><code>$kakaoInformationSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr><tr><td><code>$textMarketingSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr><tr><td><code>$textInformationSubscribe</code></td><td>CRM</td><td><code>subscribe</code> / <code>unsubscribe</code></td></tr></tbody></table>

`push` refers to app push, `kakao` to Kakao Alimtalk/Friendtalk and the like, and `text` to text message (SMS/LMS/MMS) subscription.\
`Marketing` refers to consent for promotional messages, and `Information` to consent for informational messages.

#### Value Rules

**Subscription (`$~~Subscribe`)**

Only `subscribe` (consent) or `unsubscribe` (rejection) can be entered.

* Values are not case-sensitive. `Subscribe`, `SUBSCRIBE`, and `UnSubscribe` are all accepted.
* Values such as `Y`, `N`, `동의`, `1`, `0`, and `true` are not accepted, and the entire row is not applied.

**Mobile Phone Number (`$phoneNumber`)**

Only **mobile phone numbers** can be entered. You can use any notation format, and the value is converted to the international standard (E.164) when stored.

| Input           | Result                                                       |
| --------------- | ------------------------------------------------------------ |
| `01012345678`   | ✅                                                            |
| `010-1234-5678` | ✅ Separators such as hyphens and spaces can be used          |
| `+821012345678` | ✅ Can be entered with a country code                         |
| `+14155552671`  | ✅ International numbers are also allowed with a country code |
| `0212345678`    | ❌ Landline numbers (area codes) are not supported            |

* If there is no country code, the number is **treated as a domestic (South Korean) number**. For international numbers, be sure to include `+` and the country code.
* Landline numbers and representative numbers (15xx, 16xx, etc.) are not supported.

**Identifier (`$userId`)**

* This is a required item, and rows with no value or a value consisting only of whitespace are not applied.
* Leading/trailing whitespace and line breaks are removed automatically.

#### If a Reserved Word Is Entered Incorrectly

If it differs from the reserved word **by even a single character, it is treated as a custom property**. Value validation is not performed either.

| Column Name               | Result                                                            |
| ------------------------- | ----------------------------------------------------------------- |
| `$textMarketingSubscribe` | CRM · Text message promotional subscription                       |
| `$txtMarketingSubscribe`  | ⚠️ CUSTOM · Custom property with the key `$txtMarketingSubscribe` |

Be sure to **check in the Column Mapping step** whether the columns are mapped to the intended fields. You can also skip reserved words and specify the mapping directly in the Column Mapping step.

#### Length Limits

| Item                                   | Limit                  |
| -------------------------------------- | ---------------------- |
| Key (column name) of a custom property | Up to 128 characters   |
| Value                                  | Up to 1,024 characters |

If the limit is exceeded, the entire row is not applied.

#### Example

```
$userId,$phoneNumber,$textMarketingSubscribe,회원등급,가입일
user_001,010-1234-5678,subscribe,GOLD,20260708
user_002,01098765432,unsubscribe,SILVER,20260715
```

`회원등급` and `가입일` are not reserved words, so they are stored as custom properties with the keys `회원등급` and `가입일`, respectively.

***

## Q\&A

* Q) If I make multiple CSV upload requests at the same time, is the order guaranteed?
  * A) No, it is not guaranteed. If the order between requests matters, we recommend waiting until one request finishes processing before making the next one.
* Q) Within a single CSV file, is the order of rows that have the same userId guaranteed?
  * A) No, it is not guaranteed. If the order matters, we recommend not including the same userId multiple times in one file.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.hackle.io/en/user-view/csv-import.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
