# Help Center

Welcome to CSVbox documentation and help site!

{% content-ref url="/pages/-MOGv6-aW2aB2fgDl2Eo" %}
[Getting Started](/getting-started)
{% endcontent-ref %}

{% content-ref url="/pages/-MOWEuK-7avfvNFjjs4K" %}
[Account](/account)
{% endcontent-ref %}

{% content-ref url="/pages/-MOWJnVTWdE9JyQdSTtH" %}
[Broken mention](broken://pages/-MOWJnVTWdE9JyQdSTtH)
{% endcontent-ref %}


# Getting Started

With csvbox.io you can add a production-ready import feature to your web app in just a few minutes.

## Key Concepts

Here are a few important terms used throughout the application:

**User:** Any person who uses your application.

**File:** The spreadsheet file that the users want to upload to your application.

{% hint style="info" %}
Users can upload .csv, .xlsx or .xls file formats.
{% endhint %}

**Sheet or Template:** It refers to the data modal that specifies the structure of the data you want to accept. You can add columns to the sheet and configure validation criteria via your csvbox.io dashboard.&#x20;

{% hint style="info" %}
Users will be able to match the headers of their file columns with the sheet columns and clean data before uploading.
{% endhint %}

**Import:** The entire process where the user invokes the csvbox.io importer to select the file, match columns, validate data, and submit the file is called Import.

**Destination:** It is the end location where the csvbox.io importer will push the data uploaded by the user.

{% hint style="info" %}

1. This importer uses the PapaParse library to parse spreadsheet files. © 2014-present Matias Meno. Licensed under the MIT License.
2. The project also uses the SheetJS Community Edition (XLSX.js), an open-source library for handling spreadsheet files. © 2012-present SheetJS LLC. Licensed under the Apache License, Version 2.0.
   {% endhint %}

## Quick Start

The following are the basic steps to install the csvbox.io CSV importer widget into your app and start accepting data from your users.

1. [Add Template](/getting-started/1.-add-template) - Define the model of the incoming data.
2. [Install Code](/getting-started/2.-install-code) - Add the code snippet to your app to embed the widget.
3. [Receive Data](/getting-started/3.-receive-data) - Accept CSV data into your application.


# 1. Add Template

Configure the data model via your Csvbox dashboard.

Templates (or sheets) define the structure of the data that you want to accept from the users.

Go to the 'Sheets' page in your dashboard. Add a sheet and specify columns. Configure the validation rules and data format for each column.

{% hint style="info" %}
When users attempt to upload a file, the importer will validate the data for every column and inform the users if there are any errors. Users have to resolve the errors before submitting the file.
{% endhint %}

Under the 'Data Destination' tab, configure the destination where you want the data to be pushed. The destination could be an API endpoint, Amazon S3 bucket, MYSQL database, or any of the other options mentioned [here](https://help.csvbox.io/destinations).&#x20;


# 2. Install Code

Add a snippet of code to your app to start accepting spreadsheets from your users.

Go to the 'Code' tab of the sheet and find the integration code. Place the code in your application at the location you want to display the import button.

{% tabs %}
{% tab title="Javascript" %}
Sample code with basic usage:

```javascript
<button class="btn btn-primary" data-csvbox disabled onclick="importer.openModal();">Import</button>
<script type="text/javascript" src="https://js.csvbox.io/script.js"></script>
<script type="text/javascript">
    function callback(result, data) {
        if(result){
            console.log("success");
            console.log(data.row_success + " rows uploaded");
            //custom code
        }else{
            console.log("fail");
            //custom code
        }
    }
    let importer = new CSVBoxImporter("YOUR_LICENSE_KEY_HERE",{        
    }, callback);
    
    importer.setUser({
        user_id: "default123"
    })
</script>
```

{% hint style="info" %}
Each sheet has a unique Licence Key. Find the Licence Key of the sheet on the 'Code' tab of the sheet page and pass it to the **CSVBoxImporter** function.
{% endhint %}
{% endtab %}

{% tab title="React" %}
Install using npm:

```javascript
npm install @csvbox/react
```

&#x20;This will give you access to the **`CSVBoxButton`** component having the basic functionality of our importer. Import the **`CSVBoxButton`** component to your project.

```javascript
import { CSVBoxButton } from '@csvbox/react'
```

Basic usage:

```javascript
<CSVBoxButton
  licenseKey="YOUR_LICENSE_KEY_HERE"
  user={{
    user_id: "default123"
  }}
  onImport={(result, data) => {
    if(result){
      console.log("success");
      console.log(data.row_success + " rows uploaded");
      //custom code
    }else{
      console.log("fail");
      //custom code
    }
  }} 
  render={(launch, isLoading)=>{
          return <button disabled={isLoading} onClick={launch}>Upload file</button>;
      }}
>
  Import
</CSVBoxButton>
```

{% hint style="info" %}
Each sheet has a unique Licence Key. Find the Licence Key of the sheet on the Code section of the sheet page and attach it to the **licenseKey** property of the **CSVBoxButton** component.
{% endhint %}

{% hint style="info" %}
The optional **render** property allows you to pass in your button to use in place of the standard csvbox element.
{% endhint %}
{% endtab %}

{% tab title="Angular" %}
Install using npm:

```javascript
npm install @csvbox/angular
```

{% hint style="info" %}
For Angular versions below 12 use @csvbox/angular\_8 and for Angular versions 12 and above use @csvbox/angular
{% endhint %}

Import:&#x20;

Add `CSVBoxAngularModule` to your module imports.

```javascript
import { CSVBoxAngularModule } from "@csvbox/angular";

@NgModule({
  ...
  imports: [
    ...
    CSVBoxAngularModule
  ]
})
```

Once you have this setup, in your app component file, you will be able to import and access the`CSVBoxMethods` module for use.

It will bring the `csvbox-button` component into your project. Example:

```
<csvbox-button [licenseKey]="licenseKey" [imported]="imported.bind(this)" [user]="user">Import</csvbox-button>
```

Basic usage:

```javascript
@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"      
      [imported]="imported.bind(this)">
      Import
    </csvbox-button>
  `
})

export class AppComponent {

  title = 'example';
  licenseKey = 'YOUR_LICENSE_KEY_HERE';
  user = { user_id: 'default123' };

  imported(result: boolean, data: any) {
    if(result) {
      console.log("Sheet uploaded successfully");
      console.log(data.row_success + " rows uploaded");
    }else{
      console.log("There was some problem uploading the sheet");
    }
  }
}
```

{% hint style="info" %}
Each sheet has a unique Licence Key. Find the Licence Key of the sheet on the Code section of the sheet page and attach it to the **licenseKey** property of the **AppComponent**.
{% endhint %}

Styling the button:

In order to style the `<csvbox-button>` from within your parent component, ensure that your parent component has `ViewEncapsulation.None`, pass down a class to `<csvbox-button>`, and now you will be able to style the `button` one-level down.

```javascript
import { Component, ViewEncapsulation } from '@angular/core';
```

```javascript
@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"
      [dynamicColumns]="dynamicColumns"
      [imported]="Imported.bind(this)"
      class=“csvbox-btn”      
      >
      Import
    </csvbox-button>
  `,
  encapsulation: ViewEncapsulation.None,
  styles: [`
    .csvbox-btn button {        
        background: #007bff;
        border-radius: 3px;        
      }
  `],
export class AppComponent { 
    //...
}
```

{% endtab %}

{% tab title="Vuejs" %}
Install using npm:

```javascript
npm install @csvbox/vuejs
```

&#x20;This will give you access to the **`CSVBoxButton`** component. Import the **`CSVBoxButton`** component to your project.

```javascript
import { CSVBoxButton } from '@csvbox/vuejs'
```

&#x20;Now just import the **`CSVBoxButton`** and include it in your Vue `components`, and you're ready to get started.

Basic usage:

```javascript
<template>
  <div id="app">
    <CSVBoxButton 
      :licenseKey="licenseKey"
      :user="user"        
      :onImport="onImport">
      Upload File
    </CSVBoxButton>
  </div>
</template>

<script>
import { CSVBoxButton } from '@csvbox/vuejs';

export default {
  name: 'App',
  components: {
    CSVBoxButton,
  },
  data: () => ({
    licenseKey: 'YOUR_LICENSE_KEY_HERE',
    user: {
      user_id: 'default123',
    },
  }),
  methods: {    
    onImport: function (result, data) {    
       if(result){
          console.log("success");
          console.log(data.row_success + " rows uploaded");
          //custom code
      }else{
          console.log("fail");
          //custom code
      }
    }
  },
}
</script>
```

{% hint style="info" %}
Each sheet has a unique Licence Key. Find the Licence Key of the sheet on the Code section of the sheet page and attach it to the **licenseKey** property of the **CSVBoxButton** component.
{% endhint %}

> #### Implementation Demo
>
> Vue2 - <https://codesandbox.io/s/csvbox-vue2-plzl0q>
>
> Vue3 - <https://codesandbox.io/s/csvbox-vue3-vuselr>
> {% endtab %}
> {% endtabs %}

{% hint style="danger" %}
If your app/database is restricted to IP addresses on an allowlist, you will need to manually add CSVbox's addresses in order to use the importer.

You will have to whitelist the following IP addresses:

* `18.233.84.183`
* `18.213.249.53`
* `3.73.26.144`

Note that at any time, you will only see one of these addresses in use. However, the active IP address can change at any time, so you should add them all to your IP whitelist to ensure no interruptions in service.
{% endhint %}

### Referencing the user

You can configure **custom user attributes** in the installation code to identify the users in your platform and match them with their respective imports.&#x20;

{% tabs %}
{% tab title="Javascript" %}
Pass custom user attributes as input parameters to the **`setUser`**&#x6D;ethod. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in th&#x65;**`<key>: <value>`**&#x66;ormat. Example:

```javascript
 importer.setUser({
        user_id: "1a2b3c4d5e6f",
        team_id: "sales2",
        isAuthenticated: "true",
        permissionLevel: "admin",
        email: "abc@example.com"
    })
```

{% endtab %}

{% tab title="React" %}
Pass custom user attributes as an object to the **`user`**&#x70;roperty of the **`CSVBoxButton`** component. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in the **`<key>: <value>`**&#x66;ormat. Example:

```javascript
  user={{
              user_id: "1a2b3c4d5e6f",
              team_id: "sales2",
              isAuthenticated: "true",
              permissionLevel: "admin",
              email: "abc@example.com"
  }}
```

{% endtab %}

{% tab title="Angular" %}
Pass custom user attributes as an object to the **`user`**&#x70;roperty of the AppComponent. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in the **`<key>: <value>`**&#x66;ormat. Example:

```javascript
  user={
              user_id: "1a2b3c4d5e6f",
              team_id: "sales2",
              isAuthenticated: "true",
              permissionLevel: "admin",
              email: "abc@example.com"
 }
```

{% endtab %}

{% tab title="Vuejs" %}
Pass custom user attributes as an object to the **`user`**&#x70;roperty of the **`CSVBoxButton`** component. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in the **`<key>: <value>`**&#x66;ormat. Example:

```javascript
  user: {
              user_id: "1a2b3c4d5e6f",
              team_id: "sales2",
              isAuthenticated: "true",
              permissionLevel: "admin",
              email: "abc@example.com"
  },
```

{% endtab %}
{% endtabs %}

### Callback function

Once the user uploads a file the importer will return the status of the import along with metadata describing the completed import. Data is returned via two variables: **`result`** and **`data`**.&#x20;

1. **`result`** - It is of type boolean with the value **true** if the import is successful and **false** if the import fails.
2. **`data`** - It returns JSON data as shown below:

```javascript
  {
    "import_id": 79418895,
    "sheet_id": 575,
    "sheet_name": "Products Import",
    "destination_type": "webhook"
    "row_count": 100,
    "row_success": 98,
    "row_fail": 2,
    "import_status": "Partial",
    "import_starttime": 87987897897,
    "import_endtime": 90890890809,
    "original_filename": "example-1.csv",
    "raw_file": "https://file-download-link",
    "custom_fields": {
      "user_id": "Z1001"
    },
    "column_mappings": [
      { "Name": "Name" },
      { "SKU": "Product SKU" },
      { "Price": "Sale Price" },
      { "Quantity": "Inventory"},
      { "Image URL": "Img"}    
    ]
  }
```

{% hint style="info" %}
The data object will be null if the import fails
{% endhint %}

{% hint style="info" %}
You can also configure the importer to receive the entire file data in JSON format in the **data** object above. More information [here](/getting-started/3.-receive-data#data-at-the-client-side).
{% endhint %}

You can write custom code to handle the success or failure conditions client side.

{% tabs %}
{% tab title="Javascript" %}
The **`result`** and **`data`**&#x76;ariables will be available in the **`callback`**&#x66;unction that is triggered when the import is completed.

```javascript
function callback(result, data) {
        if(result){
            console.log("success");
            console.log(data.row_success + " rows uploaded");
            //custom code
        }else{
            console.log("fail");
            //custom code
        }
    }
```

{% endtab %}

{% tab title="React" %}
Pass additional options as an object to the **`user`**&#x70;roperty of the **`CSVBoxButton`** component. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in the **`<key>: <value>`**&#x66;ormat. Example:

```javascript
  user={{
              user_id: "1a2b3c4d5e6f",
              team_id: "sales2",
              isAuthenticated: "true",
              permissionLevel: "admin",
              email: "abc@example.com"
  }}
```

{% endtab %}

{% tab title="Angular" %}
The **`imported`** property provides access to the **`result`** and **`data`**&#x76;ariables via the specified callback function.

```javascript
  imported(result: boolean, data: any) {
    if(result) {
      console.log("Sheet uploaded successfully");
      console.log(data.row_success + " rows uploaded");
    }else{
      console.log("There was some problem uploading the sheet");
    }
  }
```

{% endtab %}

{% tab title="Vuejs" %}
The **`onImport`** property provides access to the **`result`** and **`data`**&#x76;ariables.

```javascript
onImport: function (result, data) {    
       if(result){
          console.log("success");
          console.log(data.row_success + " rows uploaded");
          //custom code
      }else{
          console.log("fail");
          //custom code
      }
}
```

{% endtab %}
{% endtabs %}

### Options

Here is the list of additional configuration options available with the CSVbox importer.

#### max\_rows

* Type: <mark style="background-color:blue;">number</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  Specify the maximum number of rows that a single file can import. This value excludes the headers of the file. So if the number of rows in a sheet is 101, but the first row is the header, then this file would be considered to have 100 data rows in it.

<figure><img src="/files/OG1TKJM9gtlBHgEEPzrT" alt=""><figcaption><p>Max Limit Message</p></figcaption></figure>

#### max\_rows\_allow\_submit

* Type: <mark style="background-color:blue;">boolean</mark>
* Default: <mark style="background-color:blue;">true</mark>
* Description:

  Allow or disallow submission of the permissible number of rows when the overall row count exceeds the limit. Consider row limit (**max\_rows**) is set to 5. If **max\_rows\_allow\_submit is** set to **true** then the user can upload the top 5 rows of his file. If **max\_rows\_allow\_submit is** set to **false** then the user will not be able to submit even 1 row if the overall row count exceeds **max\_rows**.

#### max\_rows\_custom\_message

* Type: <mark style="background-color:blue;">string</mark>
* Default: <mark style="background-color:blue;">null</mark>
* Description:

  Display a custom message to the user when the file row count is greater than **max\_rows**. This message will show only when **max\_rows\_allow\_submit** is set to **false**.

<figure><img src="/files/rIBTfoEcxo1aycXJG8cv" alt=""><figcaption><p>Custom Message for Max Row Limit</p></figcaption></figure>

#### min\_rows

* Type: <mark style="background-color:blue;">number</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  The minimum number of rows that is required for a single upload.&#x20;

#### min\_rows\_custom\_message

* Type: <mark style="background-color:blue;">string</mark>
* Default: <mark style="background-color:blue;">null</mark>
* Description:

  Display a custom message when the file row count is less than **min\_rows**.

#### language

* Type: <mark style="background-color:blue;">string</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  Specify the importer frontend language. This value will override the default language option configured via the csvbox dashboard. Acceptable values are:

<table><thead><tr><th width="150">Value</th><th>Language</th></tr></thead><tbody><tr><td>en</td><td>English</td></tr><tr><td>de</td><td>German</td></tr><tr><td>fr</td><td>French</td></tr><tr><td>es</td><td>Spanish</td></tr><tr><td>nl</td><td>Dutch</td></tr><tr><td>pt</td><td>Portuguese</td></tr><tr><td>th</td><td>Thai</td></tr><tr><td>pl</td><td>Polish</td></tr><tr><td>ro</td><td>Romanian</td></tr><tr><td>he</td><td>Hebrew</td></tr><tr><td>ja</td><td>Japenese</td></tr><tr><td>da</td><td>Danish</td></tr><tr><td>tr</td><td>Turkish</td></tr><tr><td>sk</td><td>Slovak</td></tr><tr><td>hi</td><td>Hindi</td></tr><tr><td>ms</td><td>Bahasa Malaysia (Malay)</td></tr><tr><td>ru</td><td>Russian </td></tr><tr><td>vi</td><td>Vietnamese </td></tr><tr><td>ko</td><td>Korean </td></tr><tr><td>id</td><td>Bahasa Indonesia</td></tr><tr><td>ar-EG</td><td>Egyptian Arabic</td></tr><tr><td>ur</td><td>Urdu </td></tr><tr><td>zh-CN</td><td>Chinese (Simplified) </td></tr><tr><td>zh-TW</td><td>Chinese (Traditional)</td></tr><tr><td>bg</td><td>Bulgarian</td></tr><tr><td>cs</td><td>Czech</td></tr><tr><td>hu</td><td>Hungarian</td></tr><tr><td>uk</td><td>Ukrainian</td></tr></tbody></table>

#### allow\_invalid

* Type: <mark style="background-color:blue;">boolean</mark>&#x20;
* Default: <mark style="background-color:blue;">0</mark>&#x20;
* Description:

  It specifies if the users can continue to submit the file even if there are validation errors.

#### request\_headers

* Type: <mark style="background-color:blue;">{ key: value, key: value, ... }</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  This is where you define additional headers that get passed with each HTTP request.

#### dynamic\_list\_request\_headers

* Type: <mark style="background-color:blue;">{ key: value, key: value, ... }</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  This is where you define additional headers that get passed with each HTTP request for the [dynamic list API](/dashboard-settings/validations#dynamic-list).

#### sample\_template\_url

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  It is the URL to download the sample CSV file for the end users.

#### sample\_template\_button\_text

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  It specifies the text on the button for downloading the sample CSV file.

{% hint style="info" %}
The **sample\_template\_url** and the **sample\_template\_button\_text** options can be used to provide the end users with a customized dynamic sample CSV file.

<img src="/files/ysOH6jHnobgNPaweOUeX" alt="" data-size="original">
{% endhint %}

<mark style="color:orange;">Examples:</mark>

{% tabs %}
{% tab title="Javascript" %}
Pass additional options as input parameters to the **`setOptions`**&#x6D;ethod.

```javascript
importer.setOptions({
    max_rows: 50,
    language: 'de',
    request_headers: {
            "Content-Type": "application/json",
            "X-Access-Token": "71ab1d73a4d1319b260e9a0sdbdbc1c"
    },
    sample_template_url: 'https://files.myapp.com/user-18768',
    sample_template_button_text: 'Starter Template' 
})
```

{% endtab %}

{% tab title="React" %}
Pass the additional options as an object to the **`options`**&#x70;roperty of the **`CSVBoxButton`** component.&#x20;

```javascript
  options={{
              max_rows: 50,
              language: 'de',     
              request_headers: {
                        "Content-Type": "application/json",
                        "X-Access-Token": "71ab1d73a4d1319b260e9a0sdbdbc1c"
                  },
              sample_template_url: 'https://files.myapp.com/user-18768',
              sample_template_button_text: 'Starter Template'              
  }}
```

{% endtab %}

{% tab title="Angular" %}
Pass the additional options as an object to the **`options`**&#x70;roperty of the AppComponent.&#x20;

```javascript
  options={
              max_rows: 50,
              language: 'de',
              request_headers: {
                        "Content-Type": "application/json",
                        "X-Access-Token": "71ab1d73a4d1319b260e9a0sdbdbc1c"
                },
              sample_template_url: 'https://files.myapp.com/user-18768',
              sample_template_button_text: 'Starter Template'                     
 }
```

{% endtab %}

{% tab title="Vuejs" %}
Pass the additional options as an object to the **`options`**&#x70;roperty of the **`CSVBoxButton`** component.&#x20;

```javascript
  options: {
              max_rows: 50,
              language: 'de',
              request_headers: {
                        "Content-Type": "application/json",
                        "X-Access-Token": "71ab1d73a4d1319b260e9a0sdbdbc1c"
                },
              sample_template_url: 'https://files.myapp.com/user-18768',
              sample_template_button_text: 'Starter Template'    
  },
```

{% endtab %}
{% endtabs %}

#### target\_file\_name

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  It specifies the name of the file that gets pushed to your destination. This is applicable for the following destinations only:

  * s3
  * FTP Server
  * Google Sheets

#### upload\_file\_url

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  The URL of the file to be imported. This is useful in cases where you want the data to be pre-loaded into the importer without asking the users to upload the file. Simply provide the file location and the importer will load the data when the user clicks the Import button.

#### upload\_file\_worksheet\_name

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  The name of the worksheet that should be uploaded if you are uploading a file with multiple tabs via [**upload\_file\_url** ](#upload_file_url)option.

#### theme

* Type: <mark style="background-color:blue;">String</mark>&#x20;
* Default: <mark style="background-color:blue;">null</mark>&#x20;
* Description:

  Initializes the importer with the specified theme. Supported values:\
  `light`, `light-custom`, `dark`, `dark-custom`. If not specified, the theme set in the sheet dashboard will be used by default.

#### **default\_header\_row**

* Type: number
* Default: 1
* Description:\
  Row to auto-select as the header on the Header Selection page (rows are 1-indexed).

### Events

Here is the list of additional importer events/properties:

#### onReady

Triggers when the importer is initialized and ready for use by the users. The users can then click the Import button to open the Importer modal dialog.

#### onLoadStart

Triggers when the importer iFrame starts loading.

{% hint style="info" %}
In vanilla Javascript, onLoadStart will trigger only when the [Lazy Load](#lazy-load) is activated. Without Lazy Load, the iframe loading starts as soon as the importer is initialized.
{% endhint %}

#### onClose

Triggers when the importer is closed.

#### onSubmit

Triggers when the user hits the 'Submit' button to upload the validated file. **`data`** object is available in this event. It contains metadata related to the import.&#x20;

<details>

<summary>onSubmit <code>Data</code> object sample</summary>

```json
{
  "import_id": 79418895,
  "sheet_id": 575,
  "sheet_name": "Products Import",
  "destination_type": "webhook",
  "row_count": 100,
  "import_starttime": 87987897897,
  "original_filename": "example-01.csv",
  "custom_fields": {
    "user_id": "Z1001"
  },
  "column_mappings": [
    {
      "Item": "Product Name"
    },
    {
      "SKU": "Product SKU"
    },
    {
      "Price": "Sale Price"
    },
    {
      "Quantity": "Inventory"
    },
    {
      "Image URL": "Img"
    }
  ]
}
```

</details>

#### onImport

Triggers when all the data is pushed to the destination. Two objects are available in this event:

1. **`result`** (boolean): It is true when the import is successful and false when the import fails.
2. **`data`** (object): Contains metadata related to the import.

#### <mark style="color:orange;">Examples:</mark>

{% tabs %}
{% tab title="Javascript" %}

```javascript
<button class="btn btn-primary" data-csvbox disabled onclick="importer.openModal();">Import</button>
<script type="text/javascript" src="https://js.csvbox.io/script.js"></script>
<script type="text/javascript">
       function callback(result, data) {
           if(result){
               console.log("Sheet uploaded successfully");
               console.log(data.row_success + " rows uploaded");
           }else{
               console.log("There was some problem uploading the sheet");
           }
       }
       let importer = new CSVBoxImporter("YOUR_LICENSE_KEY_HERE",{}, callback);
       importer.setUser({
           user_id: 'default123'
       });
       
       importer.listen("onReady", function(){
        console.log("onReady");
       });
       
       importer.listen("onClose", function(){
        console.log("onClose");
       });
       
        importer.listen("onSubmit", function(data){
        console.log("onSubmit");
        console.log(data.import_id);
       });
       
</script>    
```

{% endtab %}

{% tab title="React" %}

```javascript
import React from "react";

import { CSVBoxButton } from "@csvbox/react";

const App = () => {
  return (
    <CSVBoxButton
      licenseKey="YOUR_LICENSE_KEY_HERE"
      lazy={true}
      user={{
        user_id: "default123"
      }}
      onImport={(result, data) => {
        if (result) {
          console.log("success");
          console.log(data.row_success + " rows uploaded");
          //custom code
        } else {
          console.log("fail");
          //custom code
        }
      }}
      loadStarted={() => {
        console.log("loadStarted");
      }}
      onReady={() => {
        console.log("onReady");
      }}
      onClose={() => {
        console.log("onclosed");
      }}
      onSubmit={(data) => {
        console.log("onSubmit");
        console.log(data.import_id);
      }}
    >
      Import
    </CSVBoxButton>
  );
};

export default App;
```

{% endtab %}

{% tab title="Angular" %}
{% hint style="info" %}
The events are named slightly differently for Angular.
{% endhint %}

| Event       | Angular Event |
| ----------- | ------------- |
| onReady     | importerReady |
| onLoadStart | loadStarted   |
| onClose     | closed        |
| onSubmit    | submitted     |
| onImport    | imported      |

```javascript
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"
      [dynamicColumns]="dynamicColumns"
      [imported]="imported.bind(this)"
      [closed]="closed.bind(this)"
      [submitted]="submitted.bind(this)"
      [importerReady]="importerReady.bind(this)">
      Import
    </csvbox-button>
  `
})
export class AppComponent {

  title = 'example';
  licenseKey = 'YOUR_LICENSE_KEY_HERE';
  user = { user_id: 'default123' };
  dynamicColumns = [
    {
      column_name: 'col2',
      type: 'text'
    }
  ];

  imported(result: boolean, data: any) {
    console.log("result", result, "data", data)
    if(result) {
      console.log("Sheet uploaded successfully");
      console.log(data.row_success + " rows uploaded");
    }else{
      console.log("There was some problem uploading the sheet");
    }
  }
  
  loadStarted() {
    console.log("loadStarted");
  }

  closed(){
    console.log("onClose");
  }

  importerReady(){
    console.log("onReady");
  }

  submitted(data: any){
    console.log("onSubmit");
    console.log(data.import_id);
  }
}
```

{% endtab %}

{% tab title="Vuejs" %}

```javascript
<template>
  <div>
    <CSVBoxButton 
      licenseKey="YOUR_LICENSE_KEY_HERE" 
      :user="{ user_id: 'default123' }" 
      :onImport="onImport"
      :onReady="onReady"
      :onClose="onClose"
      :onSubmit="onSubmit"
      >Import</CSVBoxButton>

  </div>
</template>
<script>

import CSVBoxButton from './components/CSVBoxButton'

export default {
  name: 'App',
  components: {
    CSVBoxButton
  },
  methods: {
    onImport(result, data){
      console.log("onImport", result, data);
      if(result){
        console.log("success");
        console.log(data.row_success + " rows uploaded");
        //custom code
      }else{
        console.log("fail");
        //custom code
      }
    },
    onReady(){
      console.log("onReady");
    },
    onClose(){
      console.log("onClose");
    },
     onSubmit(data){
      console.log("onSubmit");
      console.log(data.import_id);
    }
  },
  mounted() {
    
  },
}
</script>
```

{% endtab %}
{% endtabs %}

### Other Settings

### Lazy Load

The importer assets are loaded on the webpage load. This can sometimes slow the app if you initialize multiple importers on the same page. As a workaround, you can defer the loading of the importer assets to the Import button click. This can be done by adding the `lazy: true` parameter to the initialization code.

{% tabs %}
{% tab title="Javascript" %}

```javascript
let importer = new CSVBoxImporter("YOUR_LICENSE_KEY_HERE",{        
    }, callback, { lazy: true });

```

{% endtab %}

{% tab title="React" %}

```javascript
import { CSVBoxButton } from '@csvbox/react'
import './App.css';

function App() {
  return (
    <div className="App">
      <CSVBoxButton
        licenseKey="YOUR_LICENSE_KEY_HERE"
        user={{
          user_id: "default123"
        }}
        onImport={(result, data) => {
          if(result){
            console.log("success");
            console.log(data.row_success + " rows uploaded");
            //custom code
          }else{
            console.log("fail");
            //custom code
          }
        }}
        lazy={true}
      >
        Import
      </CSVBoxButton>
    </div>
  );
}

export default App;
```

{% endtab %}

{% tab title="Angular" %}

```javascript
@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"
      [imported]="imported.bind(this)"
      [lazy]="lazy">
      Import
    </csvbox-button>
  `
})
export class AppComponent {
  title = 'csvbox-test';
  licenseKey = 'YOUR_LICENSE_KEY_HERE';
  user = { user_id: 'default123' };
  lazy = true;
  imported(result: boolean, data: any) {
    if(result) {
      console.log("Sheet uploaded successfully");
      console.log(data.row_success + " rows uploaded");
    }else{
      console.log("There was some problem uploading the sheet");
    }
  }
}
```

{% endtab %}

{% tab title="Vuejs" %}

```javascript
<CSVBoxButton
      :licenseKey="licenseKey"
      :user="user"      
      :onImport="onImport"
      :lazy="true">
      Upload File
</CSVBoxButton>
```

{% endtab %}
{% endtabs %}


# 3. Receive Data

Receive ready-to-use data in your app.

Once the code is installed the users will be able to submit their files via the csvbox importer. The raw files uploaded by the users will be available on your dashboard's 'Import' page. The data will also be pushed to your app as per the [destination ](/destinations)type configuration of your sheet.

#### Sample JSON response for destination API/Webhook: <a href="#sample-response" id="sample-response"></a>

```json
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "total_rows": 1009,
    "env_name": "default", 
    "original_filename": "products01_24.csv",
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3",
          "Image URL": "https://cdn.shopify.com/s/files/1/1491/9536/products/31jJOj1DS5L_070b4893-b7af-482f-8a15-d40f5e06760d.jpg?v=1521803806"
    },
    "custom_fields": {
      "user_id": "1002"
    }
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "total_rows": 1009,
    "env_name": "default", 
    "original_filename": "products01_24.csv",
    "row_data":{
          "Name": "EPower Technology EP-600PM Power Supply 600W ATX12V 2.3 Single 120mm Cooling Fan Bare",
          "SKU": "AS-103824",
          "Price": "95.35",
          "Quantity": "8",
          "Image URL": "https://cdn.shopify.com/s/files/1/1491/9536/products/71pRC5VjF-L_8f840eb9-6a47-407f-999c-490f7814159d.jpg?v=1521803806"
        },
    "custom_fields": {
      "user_id": "1002"
    }
  },
]

```

{% hint style="warning" %}
All import data and RAW files will be retained for one month. You also have the option to bypass storing the files in our data store.
{% endhint %}

### Import Complete Webhook

Optionally, you can subscribe to the import complete event webhook via the sheet settings page. This webhook will fire when the CSVbox completes the import process for any user. Here is some example JSON that could be sent to your webhook endpoint:

```json
  {
    "import_id": 79418895,
    "sheet_id": 575,
    "sheet_name": "Products Import",
    "env_name": "default", 
    "original_filename": "products01_24.csv",
    "destination_type": "webhook",
    "row_count": 100,
    "row_success": 98,
    "row_fail": 2,
    "import_status": "Partial",
    "import_starttime": 87987897897,
    "import_endtime": 90890890809,
    "original_filename": "example-01.csv",
    "raw_file": "https://file-download-link",
    "custom_fields": {
      "user_id": "Z1001"
    },
    "raw_columns": [
        "Name", "Product SKU", "Origin Date", "Sale Price", "Inventory", "Img", "Notes"
    ],
    "column_mappings": [
      { "Name": "Name" },
      { "SKU": "Product SKU" },
      { "Price": "Sale Price" },
      { "Quantity": "Inventory"},
      { "Image URL": "Img"}
    ]
  }
```

### Data on the Client Side

You also have the option to receive the entire CSV data in JSON format inside the **`data`** object of the [**`callback`**](https://help.csvbox.io/getting-started/2.-install-code#callback-function) function.\
\
To receive the CSV data in the callback function simply activate this option on the sheet '**Destination**' page.

<div align="left"><img src="/files/Zees8pK4XbtwCQsHcYcH" alt="Send JSON data to client"></div>

You will then receive the JSON data in the **`rows`** property of the **`data`** object. Check the sample below:

```json
{
  "import_id": 79418895,
  "sheet_id": 575,
  "sheet_name": "Products Import",
  "destination_type": "webhook",
  "env_name": "default", 
  "original_filename": "products01_24.csv",
  "row_count": 2,
  "row_success": 2,
  "row_fail": 0,
  "import_status": "Success",
  "import_starttime": 87987897897,
  "import_endtime": 90890890809,
  "raw_file": "https://file-download-link",
  "custom_fields": {
    "user_id": "Z1001"
  },
  "column_mappings": [
      { "Name": "Name" },
      { "SKU": "Product SKU" },
      { "Price": "Sale Price" },
      { "Quantity": "Inventory"},
      { "Image URL": "Img"},
      { "Barcode": "Barcode"},
      { "Tags": "Attributes"}
    ],
   "raw_columns": [
        "Name", "Product SKU", "Origin Date", "Sale Price", "Inventory", "Img", "Notes"
    ],
  "rows": [
    {
      "Name": "Nike Shoes Greenhorn",
      "SKU": "NSGH",
      "Price": "195.35",
      "Quantity": "2",
      "_dynamic_data": {
        "inventory": "11",
        "origin": "uk"
      },
      "_unmapped_data": {
        "Barcode": "2748937411",        
        "Tags": "green"
      }
    },
    {
      "Name": "Nike Shoes Airflow",
      "SKU": "NSAF",
      "Price": "295.35",
      "Quantity": "1",
      "_dynamic_data": {
         "inventory": "2",
         "origin": "uk"
      },
      "_unmapped_data": {
        "Barcode": "5648937421",        
        "Tags": "blue"
      }
    }
  ]
}
```


# Dashboard Settings

Configure the importer via Csvbox dashboard

1. [Column Options](/dashboard-settings/sheet-options) - Description of the options available while creating the columns of the template.
2. [Add Columns via CSV](/dashboard-settings/add-columns-via-csv) - How to add columns in bulk by uploading a CSV
3. [Validation Options](/dashboard-settings/validations) - List of available validation options for checking the format of the incoming data.
4. [Sheet Options](/dashboard-settings/sheet-options-1) - Description of select sheet configuration settings.


# Column Options

Description of column configuration settings

## Column Name

`required`

The Column Name is the field name key that will be pushed to the data destination.

## Display Label

`optional`

Display Labels will be shown in the header row that the user will see while doing an import. Each Display Label internally maps to a Column Name. While the Display Label will be seen by the users inside the import widget, the corresponding Column Name will be pushed to the destination as the field name. If Display Label is not specified, then the Column Name will be shown to the users by default. This helps you to display a user-friendly label to the users while pushing a column name that your app understands. For example, 'p\_id' can be the Column Name and 'Product ID' can be the Display Label.

## Info Hint

`optional`

Info Hints are help tooltips that will get displayed when the users hover the mouse over the Column Name (or click it) in the importer. They are useful to convey additional information about the Column.

![Info Hint](/files/-MkGvnzpPww__QcgsmJR)

## Matching Keywords

`optional`

You can provide a set of keywords as alternative matching options to help users match column names automatically. For example, let's say you have a column name 'First Name'. If you think a lot of your users might have sheets with columns as 'F\_Name' or simply 'First', then you can add two matching keywords 'F\_Name' and 'First'. The importer will then automatically match columns to the specified keywords to speed up column mapping.

{% hint style="info" %}

#### **User Keywords**

Apart from Matching Keywords, CSVBox auto-maps columns via continuous learning.

The importer remembers the columns mapped by the users.  It then uses the historical mappings to automatically map columns for new uploads. This way the importer adapts and collects new **User Keywords** as more sheets get uploaded.

You have the option to view and delete the User Keywords via Column Settings.

![](/files/zZRKlYIZn1cuhYXdmbcF)

You also have the option to disable the user keywords-based column mapping completely. Go to the **Sheets** page > **Options** tab > Select Enable or Disable **User Keywords for Column Mapping**.&#x20;

![](/files/L1lWj9QEWxfWpg370j6q)
{% endhint %}

## Default Value

`optional`

You can specify a default filler value for the column in case the incoming data is blank.

## Column Type

`required`

This option helps you specify the data type of the incoming data and configure relevant validation rules. You can select the column type from the dropdown and set conditions for how the data should be formatted. If the incoming data does not match the column type (and its validation rules), then the user will see a relevant error message identifying what the problem is and how to fix it. This ensures that the data is clean and ready to use before it gets pushed to your app.

## Required

`optional`

The Required checkbox indicates whether a column is mandatory. If the Required checkbox is ticked, then the importer will validate the column for missing/empty data. If any cell in the column is found to be empty then the user will be shown a relevant error message.

## Read Only

`optional`

Enabling this option will make the column read-only. The column data will be visible yet the end users will not be able to edit it on the Verify Data step.


# Bulk Add Columns

Bulk add columns to a new sheet by providing the column info in a CSV file.

CSVbox offers two methods to do this:

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

### 1. Using Sample CSV File

Upload any sample CSV or Excel file you already have. We’ll automatically extract the column headers from your file and create corresponding columns in your sheet. Our AI will attempt to detect column types based on the data. You can always review and modify the columns afterward.

### 2. Using Bulk Import Template

If you prefer more control over column setup, use our **predefined CSV template**. Simply download the template, fill it with column info, and upload it back. Columns will be created based on the data you provide. The template supports the following fields:

<table><thead><tr><th width="150">Field</th><th width="453">Description</th><th>Required</th></tr></thead><tbody><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#column-name">Column Name</a></td><td>Internal name of the column</td><td>✅ Yes</td></tr><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#display-label">Display Name</a></td><td>Display label for end users</td><td>Optional</td></tr><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#info-hint">Info Hint</a></td><td>Tooltip/help text for users</td><td>Optional</td></tr><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#column-type">Column Type</a></td><td>Type of the field (e.g. Text, Number, Date)</td><td>Optional</td></tr><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#required">Is Required</a></td><td>Should this field be mandatory? (Yes/No)</td><td>Optional</td></tr><tr><td><a href="https://help.csvbox.io/getting-started/sheet-options#matching-keywords">Keywords</a></td><td>Tags to help with auto-mapping</td><td>Optional</td></tr></tbody></table>

**Download Template:**

{% file src="/files/8NlKY89UaBeKlZH59CiT" %}
Bulk Import Template
{% endfile %}


# Validations

This page describes the various validation options available inside the importer.

## Column Data Types

While creating a sheet you can specify the column to be any of the data types mentioned below. If the incoming CSV data does not match the column data type (and its validation rules), then the user will see a relevant error message identifying what the problem is and how to fix it. &#x20;

### Text

This is the default column data type. It accepts any alphanumeric string. You can provide the `Min Length` and `Max Length` parameters to specify the acceptable length of the text data.

### Number

This **Number** column data type accepts integer and float strings only. Additionally, you can provide the `Min Value` and `Max Value` parameters to specify the acceptable range of numbers.

<details>

<summary>Values for Excel Setting</summary>

When importing data from Excel files, you may encounter unexpected issues due to the way Excel internally stores and formats different types of values—especially **Numbers**. Excel often applies formatting to numeric values that changes how they appear:

* A raw value of `0.15` could be displayed as `15%`
* A value of `1200` might appear as `$1,200.00`
* A long number like `1234567890123` may be auto-converted to scientific notation (`1.23E+12`)

These formatted displays can mislead the importer if you're expecting clean numbers. With CSVbox you have the following options to select the way the values are interpretated for numeric columns.

1. **Formatted Values** Imports numbers exactly as shown in Excel. Examples:
   * `$1,200.00` stays `$1,200.00`
   * `15%` stays `15%`
   * `1.23E+12` stays in scientific notation
2. **Original RAW Values** Strips all formatting and imports the core numeric value. Examples:
   * `$1,200.00` → `1200`
   * `15%` → `0.15`
   * `1.23E+12` → `1230000000000`

{% hint style="info" %}
Use RAW if you’re planning to run calculations or validations on numeric fields post-import.
{% endhint %}

</details>

### Email

The column under validation must be formatted as an email address.

### Date

The column under validation must be formatted as a date. You have to select a `Date Format` from the dropdown list to specify the acceptable format. The default format is '*MM/DD/YYYY*'. If your desired format is not found in the list, you can simply provide your custom format using the formatting tokens below.

|                                | Token              | Output                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Month**                      | M                  | 1 2 ... 11 12                                                                                                                                                                                                                                                                                                                              |
|                                | Mo                 | 1st 2nd ... 11th 12th                                                                                                                                                                                                                                                                                                                      |
|                                | MM                 | 01 02 ... 11 12                                                                                                                                                                                                                                                                                                                            |
|                                | MMM                | Jan Feb ... Nov Dec                                                                                                                                                                                                                                                                                                                        |
|                                | MMMM               | January February ... November December                                                                                                                                                                                                                                                                                                     |
| **Quarter**                    | Q                  | 1 2 3 4                                                                                                                                                                                                                                                                                                                                    |
|                                | Qo                 | 1st 2nd 3rd 4th                                                                                                                                                                                                                                                                                                                            |
| **Day of Month**               | D                  | 1 2 ... 30 31                                                                                                                                                                                                                                                                                                                              |
|                                | Do                 | 1st 2nd ... 30th 31st                                                                                                                                                                                                                                                                                                                      |
|                                | DD                 | 01 02 ... 30 31                                                                                                                                                                                                                                                                                                                            |
| **Day of Year**                | DDD                | 1 2 ... 364 365                                                                                                                                                                                                                                                                                                                            |
|                                | DDDo               | 1st 2nd ... 364th 365th                                                                                                                                                                                                                                                                                                                    |
|                                | DDDD               | 001 002 ... 364 365                                                                                                                                                                                                                                                                                                                        |
| **Day of Week**                | d                  | 0 1 ... 5 6                                                                                                                                                                                                                                                                                                                                |
|                                | do                 | 0th 1st ... 5th 6th                                                                                                                                                                                                                                                                                                                        |
|                                | dd                 | Su Mo ... Fr Sa                                                                                                                                                                                                                                                                                                                            |
|                                | ddd                | Sun Mon ... Fri Sat                                                                                                                                                                                                                                                                                                                        |
|                                | dddd               | Sunday Monday ... Friday Saturday                                                                                                                                                                                                                                                                                                          |
| **Day of Week (Locale)**       | e                  | 0 1 ... 5 6                                                                                                                                                                                                                                                                                                                                |
| **Day of Week (ISO)**          | E                  | 1 2 ... 6 7                                                                                                                                                                                                                                                                                                                                |
| **Week of Year**               | w                  | 1 2 ... 52 53                                                                                                                                                                                                                                                                                                                              |
|                                | wo                 | 1st 2nd ... 52nd 53rd                                                                                                                                                                                                                                                                                                                      |
|                                | ww                 | 01 02 ... 52 53                                                                                                                                                                                                                                                                                                                            |
| **Week of Year (ISO)**         | W                  | 1 2 ... 52 53                                                                                                                                                                                                                                                                                                                              |
|                                | Wo                 | 1st 2nd ... 52nd 53rd                                                                                                                                                                                                                                                                                                                      |
|                                | WW                 | 01 02 ... 52 53                                                                                                                                                                                                                                                                                                                            |
| **Year**                       | YY                 | 70 71 ... 29 30                                                                                                                                                                                                                                                                                                                            |
|                                | YYYY               | 1970 1971 ... 2029 2030                                                                                                                                                                                                                                                                                                                    |
|                                | YYYYYY             | <p>-001970 -001971 ... +001907 +001971<br><strong>Note:</strong> <a href="https://tc39.es/ecma262/#sec-expanded-years">Expanded Years</a> (Covering the full time value range of approximately 273,790 years forward or backward from 01 January, 1970)</p>                                                                                |
|                                | Y                  | <p>1970 1971 ... 9999 +10000 +10001<br><strong>Note:</strong> This complies with the ISO 8601 standard for dates past the year 9999</p>                                                                                                                                                                                                    |
| **Era Year**                   | y                  | 1 2 ... 2020 ...                                                                                                                                                                                                                                                                                                                           |
| **Era**                        | N, NN, NNN         | <p>BC AD<br><strong>Note:</strong> Abbr era name</p>                                                                                                                                                                                                                                                                                       |
|                                | NNNN               | <p>Before Christ, Anno Domini<br><strong>Note:</strong> Full era name</p>                                                                                                                                                                                                                                                                  |
|                                | NNNNN              | <p>BC AD<br><strong>Note:</strong> Narrow era name</p>                                                                                                                                                                                                                                                                                     |
| **Week Year**                  | gg                 | 70 71 ... 29 30                                                                                                                                                                                                                                                                                                                            |
|                                | gggg               | 1970 1971 ... 2029 2030                                                                                                                                                                                                                                                                                                                    |
| **Week Year (ISO)**            | GG                 | 70 71 ... 29 30                                                                                                                                                                                                                                                                                                                            |
|                                | GGGG               | 1970 1971 ... 2029 2030                                                                                                                                                                                                                                                                                                                    |
| **AM/PM**                      | A                  | AM PM                                                                                                                                                                                                                                                                                                                                      |
|                                | a                  | am pm                                                                                                                                                                                                                                                                                                                                      |
| **Hour**                       | H                  | 0 1 ... 22 23                                                                                                                                                                                                                                                                                                                              |
|                                | HH                 | 00 01 ... 22 23                                                                                                                                                                                                                                                                                                                            |
|                                | h                  | 1 2 ... 11 12                                                                                                                                                                                                                                                                                                                              |
|                                | hh                 | 01 02 ... 11 12                                                                                                                                                                                                                                                                                                                            |
|                                | k                  | 1 2 ... 23 24                                                                                                                                                                                                                                                                                                                              |
|                                | kk                 | 01 02 ... 23 24                                                                                                                                                                                                                                                                                                                            |
| **Minute**                     | m                  | 0 1 ... 58 59                                                                                                                                                                                                                                                                                                                              |
|                                | mm                 | 00 01 ... 58 59                                                                                                                                                                                                                                                                                                                            |
| **Second**                     | s                  | 0 1 ... 58 59                                                                                                                                                                                                                                                                                                                              |
|                                | ss                 | 00 01 ... 58 59                                                                                                                                                                                                                                                                                                                            |
| **Fractional Second**          | S                  | 0 1 ... 8 9                                                                                                                                                                                                                                                                                                                                |
|                                | SS                 | 00 01 ... 98 99                                                                                                                                                                                                                                                                                                                            |
|                                | SSS                | 000 001 ... 998 999                                                                                                                                                                                                                                                                                                                        |
|                                | SSSS ... SSSSSSSSS | 000\[0..] 001\[0..] ... 998\[0..] 999\[0..]                                                                                                                                                                                                                                                                                                |
| **Time Zone**                  | z or zz            | <p>EST CST ... MST PST<br><strong>Note:</strong> as of <strong>1.6.0</strong>, the z/zz format tokens have been deprecated from plain moment objects. <a href="https://github.com/moment/moment/issues/162">Read more about it here.</a> However, they *do* work if you are using a specific time zone with the moment-timezone addon.</p> |
|                                | Z                  | -07:00 -06:00 ... +06:00 +07:00                                                                                                                                                                                                                                                                                                            |
|                                | ZZ                 | -0700 -0600 ... +0600 +0700                                                                                                                                                                                                                                                                                                                |
| **Unix Timestamp**             | X                  | 1360013296                                                                                                                                                                                                                                                                                                                                 |
| **Unix Millisecond Timestamp** | x                  | 1360013296123                                                                                                                                                                                                                                                                                                                              |

{% hint style="info" %}
For example, specifying a token string **`dddd, MMMM Do YYYY, h:mm:ss`**  will require the date to be in the format **`Sunday, February 14th 2010, 3:25:50 pm`.**
{% endhint %}

### Time

The column must be formatted in a time format. You have to select a `Time Format` from the dropdown list to specify the acceptable format. The default format is '*HH:mm:ss*'. If your desired format is not found in the list, you can simply provide your custom format using the formatting tokens below.

|                                | Token              | Output                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **AM/PM**                      | A                  | AM PM                                                                                                                                                                                                                                                                                                                                      |
|                                | a                  | am pm                                                                                                                                                                                                                                                                                                                                      |
| **Hour**                       | H                  | 0 1 ... 22 23                                                                                                                                                                                                                                                                                                                              |
|                                | HH                 | 00 01 ... 22 23                                                                                                                                                                                                                                                                                                                            |
|                                | h                  | 1 2 ... 11 12                                                                                                                                                                                                                                                                                                                              |
|                                | hh                 | 01 02 ... 11 12                                                                                                                                                                                                                                                                                                                            |
|                                | k                  | 1 2 ... 23 24                                                                                                                                                                                                                                                                                                                              |
|                                | kk                 | 01 02 ... 23 24                                                                                                                                                                                                                                                                                                                            |
| **Minute**                     | m                  | 0 1 ... 58 59                                                                                                                                                                                                                                                                                                                              |
|                                | mm                 | 00 01 ... 58 59                                                                                                                                                                                                                                                                                                                            |
| **Second**                     | s                  | 0 1 ... 58 59                                                                                                                                                                                                                                                                                                                              |
|                                | ss                 | 00 01 ... 58 59                                                                                                                                                                                                                                                                                                                            |
| **Fractional Second**          | S                  | 0 1 ... 8 9                                                                                                                                                                                                                                                                                                                                |
|                                | SS                 | 00 01 ... 98 99                                                                                                                                                                                                                                                                                                                            |
|                                | SSS                | 000 001 ... 998 999                                                                                                                                                                                                                                                                                                                        |
|                                | SSSS ... SSSSSSSSS | 000\[0..] 001\[0..] ... 998\[0..] 999\[0..]                                                                                                                                                                                                                                                                                                |
| **Time Zone**                  | z or zz            | <p>EST CST ... MST PST<br><strong>Note:</strong> as of <strong>1.6.0</strong>, the z/zz format tokens have been deprecated from plain moment objects. <a href="https://github.com/moment/moment/issues/162">Read more about it here.</a> However, they *do* work if you are using a specific time zone with the moment-timezone addon.</p> |
|                                | Z                  | -07:00 -06:00 ... +06:00 +07:00                                                                                                                                                                                                                                                                                                            |
|                                | ZZ                 | -0700 -0600 ... +0600 +0700                                                                                                                                                                                                                                                                                                                |
| **Unix Timestamp**             | X                  | 1360013296                                                                                                                                                                                                                                                                                                                                 |
| **Unix Millisecond Timestamp** | x                  | 360013296123                                                                                                                                                                                                                                                                                                                               |

### Boolean

The column under validation must be able to be cast as a boolean. Accepted inputs are *true*, *false*, *TRUE*, *FALSE*, *1*, *0*, *yes*, *no*, *y*, *n*, *on*, *off*, *enabled,* and *disabled*.

### Regex

The column data must match the given regular expression. You need to specify the `Regex` and the importer will pattern match it with the incoming data.&#x20;

### IP

The column under validation must be an IP address. You have to select the `IP Version`.

### URL

The column under validation must be a valid URL.

### Credit Card

The column under validation must be formatted as a credit card number. Acceptable formats are *5555555555554444*, *5555-5555-5555-4444* and *5555 5555 5555 4444*.

### Phone Number

The column will be validated for phone number formats based on the [libphonenumber.js library](https://catamphetamine.gitlab.io/libphonenumber-js/). You have to select a default country code. If the incoming phone number does not have a country code then this default country code will be used for validation.

### Currency

The column data should be in a currency amount format. You can specify currency formatting options based on the [Validator.js library](https://github.com/validatorjs/validator.js).&#x20;

### List

With the **List** data type, you can specify a list of acceptable values. The importer will compare the CSV column data with the list of acceptable values and throw a validation error if there is a mismatch. You can specify a list of acceptable `Values` and related `Display Labels`. The importer will match the column data with the list of `Display Labels`. If the data passes validation then the `Values` corresponding to the `Display Labels` will be pushed to the destination. For example say, you configure the List column as shown below:

![List Data Type](/files/DaZsiK84Vbcr9bAE6ynF)

In this case, the acceptable data in the CSV column will be *Small*, *Medium*, and *Large*. Based on the actual data found in the user CSV, the values *s*, *m* or *l* will be pushed to your data destination. This allows you to accept readable values from your users while pushing data to your system that is in a format it understands.

{% hint style="info" %}
**Allow Other Values**

<img src="/files/COMsQDhkQbgigArRi7Xa" alt="" data-size="original">

Selecting the 'Accept Other Values' option will allow the users to input values that are not found in the predefined list of acceptable values.
{% endhint %}

{% hint style="info" %}

#### Accept List Values

If you enable this option, the importer will accept List **Values** interchangeably with **Display Labels** as valid data for the column.

![](/files/lNlll8tFAwSnXSKDAiF7)

Refer to the list of items in the image above. If the column contains the value "NYC," the importer will fail validation since it only accepts the display label "New York City." To enable the importer to accept "NYC" as well, you need to activate the "Accept List Values" option.
{% endhint %}

### Dependent List

Sometimes, you may want to use more than one list such that the items available in a second drop-down list dependent on the selection made in the first drop-down list.

Let us call them dependent lists. Below is an example of a dependent list.

![](/files/D3iI57D3R5GiSIaFjWA1)

You can see that the options in the City column depend on the selection made in the Country column. If you select 'USA' in Drop Down 1, then you will cities from the USA, but if you select Canada in Drop Down 1, then you see the cities from Canada in Drop Down 2.

The same functionality can be achieved in CSVbox via a combination of [List](#list) and Dependent List column types (or a combination of [Dynamic List](#dynamic-list) and [Dependent Dynamic List](#dependent-dynamic-list) column types).

Here are the steps to create conditional lists using the example above:

1. Create a column 'Country'. Configure column type as 'List'.
2. Create a column 'City'. Configure column type as 'Dependent List'. Select 'Country' as the Primary column.\ <img src="/files/AwrY3TFxpK0fFFBIZywc" alt="" data-size="original">
3. Go back to the 'Country' column and add the list of valid values.&#x20;
4. For each valid country value, you can add dependent (city) values as shown below. ![](/files/vgPpsGWEGZsaP4VTx3tE)

{% hint style="warning" %}
If you change the name of the primary column or reorder the priority of any of the columns, then you need to reassign the primary column in the Dependent List column settings.
{% endhint %}

{% hint style="warning" %}
The Display Label and the Value should be unique across all the items. Primary items cannot share dependents having the same Display Label and/or same Value.
{% endhint %}

### Dynamic List

This is similar to the **List** type column above where the importer will validate the column data against a list of values. However, instead of providing a static list of values, with the **Dynamic List** column type, you can now specify a list of acceptable values in real-time via an API.

The API should return the list of values in the JSON format as shown below.&#x20;

```json
[
   {"value": "uk", "display_label": "England"},
   {"value": "us", "display_label": "United States"},
   {"value": "au", "display_label": "Australia"}
]
```

For each list item, the `value` field is mandatory while `display_label` is optional.

{% hint style="info" %}
You have the option to attach the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) as query parameters to the Dynamic List API request. **csvbox\_** prefix will be added to the custom user attribute query parameters. This will help you identify the users/environment and return back a relevant list of values.
{% endhint %}

For authenticating the requests you can pass the authorization headers via the [**dynamic\_list\_request\_headers** ](/getting-started/2.-install-code#dynamic_list_request_headers)initialization option.

### Dependent Dynamic List

Sometimes, you may want to use more than one list such that the items available in a second drop-down list depend on the selection made in the first drop-down list.

Let us call them dependent lists. Below is an example of a dependent list.

<div align="left"><img src="/files/Bnu3UhzaMKpfQAsg4apU" alt="Conditional dependent list"></div>

You can see that the options in the City column depend on the selection made in the Country column. If you select 'USA' in Drop Down 1, then you will cities from the USA, but if you select Canada in Drop Down 1, then you see the cities from Canada in Drop Down 2.

The same functionality can be achieved in CSVbox via a combination of [Dynamic List](#dynamic-list) and Dependent Dynamic List column types (or a combination of [List](#list) and [Dependent List](#dependent-list) column types).

Here are the steps to create conditional lists using the example above:

1. Create a column 'Country'. Configure column type as 'Dynamic List'
2. Create a column 'City'. Configure column type as 'Dependent Dynamic List'. Select 'Country' as the Primary column.\ <img src="/files/AwrY3TFxpK0fFFBIZywc" alt="" data-size="original">
3. Go back to the 'Country' column and configure the API that will fetch valid values from your application for 'Country' as well as its dependent column i.e. City column. Your API should return the list of values in the JSON format as shown below.&#x20;

```json
[
   {"value": "USA", "display_label": "USA", "dependents": [
      {"value": "ny", "display_label": "New York"},
      {"value": "ch", "display_label": "Chicago"},
      {"value": "se", "display_label": "Seatle"},
      {"value": "mi", "display_label": "Miami"}
   ]},
   {"value": "Canada", "display_label": "Canada", "dependents": [
      {"value": "to", "display_label": "Toronto"},
      {"value": "va", "display_label": "Vancouver"}    
   ]}
]
```

Note the **`dependents`** object above. It contains the list of valid values for the dependent column based on the primary column value.

{% hint style="warning" %}
If you change the name of the primary column or reorder the priority of any of the columns, then you need to reassign the primary column in the Dependent Dynamic List column settings.
{% endhint %}

{% hint style="warning" %}
The Display Label and the Value should be unique across all the items. Primary items cannot share dependents having the same Display Label and/or same Value.
{% endhint %}

### Multi-select List

The **Multi-select List** data type is similar to the [**List**](#list) data type, where you can specify a list of acceptable values. While the **List** data type accepts only one value per cell, the **Multi-select List** accepts multiple comma-separated values.

<figure><img src="/files/R6GlWYxTItc6PWlkwk0O" alt=""><figcaption><p>Multi-Select List</p></figcaption></figure>

The importer will compare the values in the incoming data with the list of acceptable values and throw a validation error if there is a mismatch.

{% hint style="info" %}
**Allow Other Values**

<img src="/files/KG1tINkA6gaNTuVSvTKg" alt="" data-size="original">

Selecting the 'Accept Other Values' option will allow the users to input values that are not found in the predefined list of acceptable values.
{% endhint %}

### Dynamic Multi-select List

This is similar to the **Multi-select** **List** type column above where the importer accepts multiple comma-separated values. However, instead of providing a static list of values, here, you can now specify a list of acceptable values in real time via an API.

The API should return the list of values in the JSON format as shown below.&#x20;

```json
[
   {"value": "Red"},
   {"value": "Green"},
   {"value": "Blue"}
]
```

{% hint style="info" %}
The importer will attach the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) as query parameters to the Dynamic List API request. **csvbox\_** prefix will be added to the custom user attribute query parameters. This will help you identify the users/environment and return back a relevant list of values.
{% endhint %}

For authenticating the requests you can pass the authorization headers via the [**dynamic\_list\_request\_headers** ](/getting-started/2.-install-code#dynamic_list_request_headers)initialization option.

## Other Validation Options

### Column Required

You can check/uncheck the `Required` checkbox on the Column Settings window. The column data must be present, and not empty if the `Required` checkbox is ticked.


# Sheet Options

Description of select sheet configuration settings

## Show Error Text

<details>

<summary>Display the import fail error messages back to the end-users.</summary>

<img src="/files/GsygGrQcCM112IGessiQ" alt="" data-size="original">

To see the errors, the users will have to click the 'See Errors' button on the import complete screen.

<img src="/files/gIUCZQfDBZ9Pgr2V8cWz" alt="" data-size="original">

</details>

## Export Button

<details>

<summary>Export validation errors in Excel</summary>

With this option, you can enable/disable the **Export** button on the verify data screen. Your users can export data to Excel while keeping the error highlighting and error messages. This helps to resolve the errors in the Excel sheet and quickly re-upload the file in CSVbox.

![](/files/Arxu7zYsdWF4IRhLshyX)

<img src="/files/uy6gOtO53LQjSFySmTCG" alt="" data-size="original">

![](/files/H0fiumbLh55rarLigFDp)

</details>

## Server & Data Location

<details>

<summary>Select the geographic location of the servers &#x26; database for the user data.</summary>

Data residency refers to where the data is stored in a geographical location. The location is important usually for regulatory or policy reasons.

You have the option to select the storage location of the data uploaded by your users.

Go to **Edit Sheets** > **Options** > **Privacy & Security** section > Select the location from the dropdown.

<img src="/files/5BzXvBanngVD8aDjY9SG" alt="" data-size="original">

The US is the default location. The other option is Europe (Germany.)

The data uploaded by the users will then pass through servers and get stored in the database situated in the selected location only.

Note, that you also have the option to not store the data at all.

The long-lived data about the import and the user files is not covered under the selected location. It mainly consists of supplementary log data helpful for troubleshooting and analyzing the import processes. This data does not include any original data from inside the user files.\
\
The image below shows how the data will flow if you select Europe as the data residency location.

<img src="/files/MeP9Cmc5CT1SsiAufnqz" alt="" data-size="original">

</details>

## Domain Authorization

<details>

<summary>Run the importer on select domains only</summary>

You can provide a list of authorized *domains/sub-domains* for embedding the importer. The embedded importer will work on the whitelisted domains only.

Go to **Edit Sheets** > **Options** > **Authorized Domains** > Add the domain/subdomains

![](/files/gseenDDFkL8aaOrOF6NG)

* If you do not whitelist any domain, then the importer embed will work on all the domains. This is the default configuration.
* You can use the "\*" wildcard prefix to include any subdomain. A few examples:

  | Text                                          | Valid                                         | Invalid                                                          |
  | --------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- |
  | exampleco.com                                 | exampleco.com                                 | [www.exampleco.com](http://www.exampleco.com), app.exampleco.com |
  | [www.exampleco.com](http://www.exampleco.com) | [www.exampleco.com](http://www.exampleco.com) | exampleco.com, app.exampleco.com                                 |
  | app.exampleco.com                             | app.exampleco.com                             | exampleco.com, [www.exampleco.com](http://www.exampleco.com)     |
  | \*.exampleco.com                              | all exampleco.com subdomains                  |                                                                  |

If a domain fails validation then the user will see the error screen as below:![](/files/FQKJrLZBPHVYiDkJU0IG)

</details>

## File Delete Policy

<details>

<summary><strong>Managing User Data Storage in CSVbox</strong></summary>

You have the option to either enable or disable the storage of the user uploaded data in CSVbox. This decision can be made based on privacy preferences or specific sheet requirements.

<img src="/files/hwg7WcvpmvzeoIicynPx" alt="" data-size="original">

* **Do not store the file**: By enabling this option, the user-uploaded data will not be stored in the CSVbox datastore.
* **Store data**: Data will be stored on CSVbox storage. It will be auto deleted after one month.

</details>

## Import Complete Messages

<details>

<summary>Show custom messages when the import is completed</summary>

You can show customized success or failure messages when the import is complete. <img src="/files/jiIygZBq0O2tvmkKveZ7" alt="" data-size="original">

The messages can be:

1. **Static** - Any fixed text as per your requirements.
2. **Dynamic** - Provide an API to fetch the message text in real-time. The importer will append metadata (`import_id`, `sheet_id`) to the API as query parameters. This will help determine the context and return relevant messages.

</details>

## Resubmit Button

<details>

<summary>1-click to resubmit the same CSV file again</summary>

With this option, you can show or hide the Resubmit button on the Import Success screen.

The Resubmit button triggers a new import pushing the same file with the same import configuration.

This is useful during testing and debugging. You don't have to upload the file, match columns and confirm data for importing the file. Simply click the Resubmit button and push the file to the same destination.

![](/files/nypQlbcYUBVpP2vL4OMg)

</details>

## Importer Dialog Size

<details>

<summary>Control the size of the import dialog box for large screens</summary>

Based on your import data structure (# of columns) you can pick between two import dialog sizes:

1. Medium

   <figure><img src="/files/u72Hjc9GUzH3Hv3zTsHp" alt=""><figcaption><p>Medium size</p></figcaption></figure>
2. Large

   <figure><img src="/files/wt4UBO0UWrsugIMVZSZs" alt=""><figcaption><p>Large size</p></figcaption></figure>

To change the size go to sheet settings > display > Importer Dialog Size

![](/files/SDmXkPhMrSkqaQ3OEKJD)

Note: The dialog size configuration will be applicable for large (desktop) screens only. For smaller screens the dialog will always occupy the entire screen.

</details>

## Worksheet Selection

<details>

<summary>Allow users to select a worksheet for upload</summary>

There can be a case where the uploaded Excel file contains multiple worksheets. You can allow the users to select a worksheet for upload.

To activate worksheet selection go to sheet settings > display > File Upload > Select '**Yes**' for **Allow Worksheet Selection** option.

<img src="/files/71GbBAssnREhBfmlYPVS" alt="" data-size="original">

If the **Allow Worksheet Selection** option is set to '**No**' then the first worksheet will be picked up by default.

</details>

## Custom Redirect URL

<details>

<summary>Redirect end users to any page after successful import</summary>

You have the ability to specify a custom URL for redirection upon successful import completion. This enhancement is designed to provide greater flexibility and streamline your workflow by directing users to a specific page immediately after the successful import.

![](/files/FtsPR0GopcgvDcQoiSnk)

</details>

## Show File Upload Box

<details>

<summary>Enable / Disable the ability to upload spreadsheet files</summary>

There can be a case where you need to disable user file uploads to allow only copy-pasting of the data in CSVbox. In such cases, you can hide the File Upload Box and only show the users the Copy-Paste data option.

To hide the File Upload Box go to sheet settings > display > File Upload > Select '**No**' for '**Show File Upload Box?**' option.

</details>

## Import Description

<details>

<summary>Allow users to provide a name/description of the file they are uploading</summary>

Enable users to input a name or a description for their uploaded files. File names like "contacts.csv" or "Import 123.xlsx" lack context. More descriptive labels such as "Texas Customers" or "Parts from 2022 Catalog" enhance the clarity and utility of the import.

The description input box will be visible if enabled after the user selects the file.\
\
&#x20;<img src="/files/fBcch9ODo8o025ZV4BhF" alt="" data-size="original">

The description will be pushed along with the row data at the end destination. The data will be available in the **import\_description** property:

```
    "import_id": 79418895,
    "sheet_id": 575,
    "sheet_name": "Products234248",
    "import_description": "Product Catalogue Jan 2024", 
    "env_name": "default", 
    "destination_type": "webhook",
```

The following destinations are supported:

1. API/Webhook
2. Zapier
3. [Data at Client](/getting-started/3.-receive-data#data-on-the-client-side)
4. [Import Complete Webhook](/getting-started/3.-receive-data#import-complete-webhook)

To enable the description input box:

Go to Sheet Settings > Display Tab > Select 'File Upload' Page > Go to 'Show import description' option > Select 'Yes'

![](/files/72PmD3lao6CN6Zabx0Ox)

By default his feature is turned OFF.

A minimum of 3 characters and a maximum of 100 characters is required.&#x20;

</details>

## Mapping Choice

<details>

<summary>Option for customers to map file columns to template fields or template fields to file columns.</summary>

#### How It Works:

By default, on the column mapping screen:

* **Template Fields** are static and displayed on the left.
* **Uploaded File Columns** appear on the right in a dropdown, allowing users to map them to the corresponding template fields.

![](/files/wxX6hyPHzEGw3cW8KWMC)

With the **Mapping Choice** option, users can **reverse this mapping direction**:

* Selecting **"Template Fields"** as the Mapping Choice flips the layout.

![](/files/ofMCTHXWodOnmiTBXj99)

* **File Columns** become static on the left, while **Template Fields** appear in the dropdown on the right, allowing users to match them accordingly.

![](/files/yyYEDn7WkNIzWBsGcyr1)

This added flexibility helps accommodate different file structures and user preferences, making the mapping process more intuitive.

{% hint style="info" %}
The **"Template Fields"** Mapping Choice is **not compatible** with the[ **Ignore Columns**](/advanced-installation/ignored-columns) functionality.
{% endhint %}

</details>

## Zero Template Columns

<details>

<summary>Accept file submissions <strong>without requiring any predefined template columns</strong></summary>

This is useful in scenarios where you want to allow users to upload files with their own structure, without enforcing a strict column format.

#### **How It Works**

* By default, CSVBox requires at least one template column for mapping.
* Configuring **"Allow Zero Template Columns"** to "Yes" lets users submit files **without any predefined template columns**, providing complete flexibility.
* This setting can be combined with the [**Unmapped Columns**](/advanced-installation/unmapped-columns) feature, allowing users to upload files with **any set of columns**, without the need for a predefined template structure.

![](/files/lHfrIJM8dSqYVmNmAPMg)

#### **When to Use This Feature**

* When you want to **fully accommodate user-defined file formats**.
* When you prefer **not to enforce a rigid template**, giving users the freedom to submit files as they are.

{% hint style="info" %}
For this feature to work effectively, ensure that **Allow Unmapped Columns** is set to **"Yes"** along with "**Show Unmapped Columns on Validation Screen" selection.** This ensures that users can review and verify file columns before submission.
{% endhint %}

</details>

## Formatting in Excel Files

<details>

<summary>Handling Excel File Formatting: Dates, Times &#x26; Numbers</summary>

When importing data from Excel files, you may encounter unexpected issues due to the way Excel internally stores and formats different types of values—especially **Dates**, **Times**, and **Numbers**. Unlike CSV files, Excel files can retain formatting, formulas, and cell types, which can lead to inconsistencies or misinterpretation during import.

To help you gain better control over how your data is interpreted, csvbox now provides customizable options to configure the way Excel data is processed.

### Date Formatting Issues in Excel

Excel stores dates in one of two ways:

1. **As formatted text**: e.g., `"03/04/2024"`
2. **As numeric serial values**: e.g., `45025`, which represents the number of days since January 1, 1900.

This dual nature can lead to problems during import, especially when dealing with international date formats. For example:

* `"03/04/2024"` might be **March 4** in the U.S. (MM/DD/YYYY)
* ...or **April 3** in Europe (DD/MM/YYYY)

If Excel stores the date as a number (`45025`) and the importer misinterprets the format, the final result could be completely off.

To address this, csvbox lets you explicitly define how Excel date values should be interpreted:

#### **Date Handling Options**

* **Default**\
  Uses Excel’s built-in logic to interpret the cell as a date. Best for files with consistent formatting and locale.
* **MM/DD/YYYY**\
  Forces interpretation using the **Month-Day-Year** format. Example:\
  `"03/04/2024"` → March 4, 2024
* **DD/MM/YYYY**\
  Forces interpretation using the **Day-Month-Year** format. Example:\
  `"03/04/2024"` → April 3, 2024
* **Custom**\
  You can define a custom format like `YYYY-MM-DD`, `DD-MMM-YYYY`, or any other pattern based on how your Excel file stores the date.\
  Example:\
  `"12-Mar-2024"` → Use custom format `DD-MMM-YYYY`

{% hint style="info" %}
If your dates are inconsistently formatted or include a mix of text and numeric formats, consider cleaning them in Excel first, or use the **Custom** option for better accuracy.
{% endhint %}

***

### Time Formatting Issues in Excel

Excel also allows time values to be stored either as:

* **Numeric fractions** of a day (e.g., `0.5` represents 12:00 PM)
* **Formatted time strings** (e.g., `"14:30"`, `"2:30 PM"`, `"14:30:00"`)

This can create parsing issues, especially if some cells are stored as numbers and others as formatted text. For example:

* `0.75` → might mean `6:00 PM` (75% of a day)
* `"06:00"` → clearly indicates 6 AM, but may be read as a string

To avoid misinterpretation, csvbox provides options for:

#### **Time Handling Options**

* **Default**\
  Uses Excel’s formatting as-is. Suitable for simple, clean files.
* **HH:MM:SS**\
  Example: `"14:30:00"` → 2:30 PM
* **HH:MM AM/PM**\
  Example: `"2:30 PM"` → 14:30
* **Custom**\
  Define your own time pattern like `HH:mm`, `h:mm a`, etc., based on your file’s content.

***

### Number Formatting Issues in Excel

Excel often applies formatting to numeric values that changes how they appear:

* A raw value of `0.15` could be displayed as `15%`
* A value of `1200` might appear as `$1,200.00`
* A long number like `1234567890123` may be auto-converted to scientific notation (`1.23E+12`)

These formatted displays can mislead the importer if you're expecting clean numbers.

#### **Number Interpretation Options**

* **Formatted Values**\
  Imports numbers exactly as shown in Excel. Examples:
  * `$1,200.00` stays `$1,200.00`
  * `15%` stays `15%`
  * `1.23E+12` stays in scientific notation
* **Original RAW Values**\
  Strips all formatting and imports the core numeric value. Examples:
  * `$1,200.00` → `1200`
  * `15%` → `0.15`
  * `1.23E+12` → `1230000000000`

{% hint style="info" %}
Use RAW if you’re planning to run calculations or validations on numeric fields post-import.
{% endhint %}

***

### How to Use These Settings

#### Configure Date Formatting

When setting up your Excel-based import:

1. Navigate to **Sheet Settings > Import Steps > File Upload Tab** in your CSVbox dashboard
2. Scroll to the **Formatting in Excel** section
3. Choose your preferred option for:
   * **Date Format**
4. Save the configuration

***

#### Configure Number Formatting

To control how numbers are interpreted from Excel files:

1. Navigate to **Sheet Settings > Columns**
2. Click **Edit** on a column with type **Number**
3. Locate the **Values for Excel** option
4. Select your preferred interpretation setting
5. Save the configuration

***

#### Notes

* Date formatting is now configured at the **sheet level during file upload setup**
* Number formatting is controlled at the **individual column level**, allowing more granular control per field

***

These new controls ensure your data is interpreted as intended—minimizing errors and reducing the need for pre-processing in Excel before importing.

</details>

## Hide Cancel Button

<details>

<summary>Hide Cancel Button</summary>

This option allows you to **hide the Cancel button** located at the bottom-right corner of the importer.

The **X (close)** button at the top-right corner of the importer remains unaffected.

Enabling this feature helps prevent users from accidentally cancelling the import process and keeps their attention focused on completing the import using the **Submit** button.

**Use case:**\
Ideal for workflows where you want to guide users toward submission without distractions or potential interruptions.

**Default value:** `No`

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

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

</details>

## Sheet Import & Export

<details>

<summary>Export a sheet from one CSVbox account and import it into another account — or duplicate it within the same account — in just a few clicks.</summary>

This is perfect for:

* Moving sheets between staging and production accounts
* Sharing templates across teams
* Backing up sheet configurations
* Reusing validated sheet structures

***

#### Export a Sheet

1. Go to **Sheets** in your dashboard.
2. Locate the sheet you want to export.
3. Click the **Export** button in the Actions column.

A dialog will open showing:

* **Sheet License Key**
* **Export Key**
* Options to **Copy**, **Show**, or **Regenerate** the export key.

Copy both the **Sheet License Key** and the **Export Key**.

***

#### Import a Sheet

1. Log in to the account where you want to import the sheet.
2. Click **Add Sheet**.
3. Select **Import Sheet**.
4. Paste:
   * The **Sheet License Key**
   * The **Export Key**
5. Click **Import**.

That’s it ✅

The entire sheet — including columns, validations, configuration, and settings — will be copied into the new account.

#### Important

* The newly imported sheet will have a **new License Key**.
* The original sheet remains unchanged.
* Importing does **not** affect existing imports or data in the original sheet.

</details>


# Styling

Personalize the importer with custom colors, fonts & logo

The **Custom Theme** option, allows you to personalize the appearance of the importer to match your brand seamlessly. With this feature, you can adjust colors, fonts, and layout sizes to create a consistent and professional look.

### **How It Works**

1. **Enable the Custom Theme** – Navigate to the importer sheets settings > Display Tab > select the **Custom Theme** option under **Importer Theme** label.
2. **Adjust Styling Parameters** – Use the **left sidebar controls** to modify colors, fonts, sizing and other styling options.
3. **Live Preview** – Click the **Preview** button to instantly see how your changes affect the importer’s appearance.
4. **Save & Apply** – Once satisfied with the customization, click **Save** to apply the theme.

<figure><img src="/files/mXYNgno308svLzzJzoTS" alt=""><figcaption><p>Styling options</p></figcaption></figure>

### **Branding Enhancements**

* **Upload Your Logo** – Add your company logo to the importer for a fully branded experience.
* **Advanced Customization** – If you require additional design tweaks beyond the provided options, [contact us](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur)—we’re happy to assist with custom styling.

{% hint style="info" %}
Custom Theme styling options are **only available for** [**Pro plan**](https://csvbox.io/#pricing) **and above.**
{% endhint %}

### Download & Reuse Importer Styling (CSS)

CSVbox now makes it easy to **reuse the same importer styling across multiple sheets or projects**.

After customizing the look and feel of your importer, you can **download the styling as a CSS file** and later **upload it to another importer** to instantly apply the same design—no rework needed.

This is especially useful if you want consistent branding across multiple imports or environments.

#### How it works

#### 1. Customize your importer styling

Go to your importer’s **Customize Styling** section and adjust colors, borders, typography, and other visual settings as needed.

#### 2. Download the styling file

Once you’re happy with the design:

* Click **Download CSS**
* CSVbox will save your current styling as a reusable `.css` file

This file contains all the visual configurations you’ve applied to the importer.

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

#### Reusing the styling on another importer

#### 3. Upload the CSS file

To apply the same styling to another sheet or importer:

1. Open the **Customize Styling** section of the new importer
2. Click **Upload CSS**
3. Select the previously downloaded CSS file

The styling will be applied instantly to the importer preview.

#### 4. Preview and save

* Review the updated look in the preview
* Click **Save** to persist the styling

That’s it—your importer now matches the original design.

#### Notes & best practices

* Uploading a CSS file **overwrites the current styling** of the importer
* You can still make further adjustments after uploading
* Always preview before saving to ensure everything looks correct
* Keep a backup of your CSS files if you maintain multiple themes


# AI Pre-Import Transform

### What it does ?

AI Pre-Import Transform lets you write a plain-English instruction that automatically restructures uploaded files before they reach the column-mapping step. Instead of asking your users to clean up their files manually, the AI rewrites the data for them, renaming columns, flattening nested headers, reordering rows, splitting or merging columns, whatever the instruction describes.

### How to enable it ?

1. Open your sheet's **Settings**
2. Go to the **File Upload** tab
3. Find **AI Pre-Import Transform** and set it to **Enabled**
4. Enter your instruction in the **Prompt** field
5. Save settings

#### Writing a prompt

The prompt is a plain-English description of what you want done to the uploaded file. Examples:<br>

* *"Rename column 'Item Description' to 'Product Name' and 'Unit Cost' to 'Price'"*<br>
* *"Flatten the two-row merged header into a single header row"*<br>
* *"Split the 'Full Name' column into 'First Name' and 'Last Name'"*<br>
* *"Remove any rows where the Quantity column is empty"*<br>
* *"Add a column called 'Status' with the value 'Pending' for e*<br>
* *very row"*\
  \
  Be specific about column names and the desired outcome. The AI follows the instruction exactly as written.

### What happens during upload

When a user uploads a file, the AI reads the data, applies your instruction, and the transformed result is what the user sees in the column-mapping step. The original file is not stored — only the transformed output is processed.

If the AI cannot apply the transformation (e.g. the file structure does not match the instruction), the original data is shown unchanged.

### Credits

AI Pre-Import Transform uses a separate credit pool from OCR credits.

| Event                                 | Credit cost                                           |
| ------------------------------------- | ----------------------------------------------------- |
| AI processes a new file               | Based on actual token usage (1 credit = 1,000 tokens) |
| Same file re-uploaded with no changes | 1 flat credit                                         |

**Monthly plan credits** are used first each billing cycle. **Purchased credits** are used as overflow once the monthly quota is exhausted.

You will receive an email notification when you reach **90%** and **100%** of your credit limit.

### Limitations

* The prompt applies to every upload on that sheet — it cannot vary per user or per file<br>
* Very large files may use more credits due to higher token count<br>
* Complex transformations (e.g. multi-condition logic) may fall back to a direct AI call, which uses more tokens than a simple recipe


# AI Bulk Transforms

Let users modify column data using natural language prompts — directly in the importer.

{% hint style="info" %}
Currently in Beta
{% endhint %}

### How to Enable AI Transformations

To enable this feature for a sheet:

1. Go to your **Sheet Settings**
2. Click on the **Import Steps** tab
3. Navigate to the **Verify Data** tab
4. Set **"Enable AI Transformations?"** to **Yes**
5. Save your settings

Once enabled, a new **"Bulk Transform"** option will appear in the Verify step of the import flow for each column.

<figure><img src="/files/PRHzM8kAhVigPdYfKb8E" alt=""><figcaption><p>Bulk Transform Button</p></figcaption></figure>

<figure><img src="/files/SO5oOuamTO4M8RGhde2R" alt=""><figcaption><p>AI Bulk Transform Screen</p></figcaption></figure>

### What Can It Do?

The users can use this tool to clean, format, and enrich the data effortlessly using plain language. It  can:

* Capitalize or lowercase text in `'product_name'`
* Add a 10% markup to values in `'price'`
* Translate `'description'` to Spanish
* Format `'order_date'` to `YYYY-MM-DD`
* Extract the domain name from `'email'`
* Tag rows as `'Yes'` in `'priority'` if `'notes'` mention "urgent"

These are just a few examples — the feature is flexible and can handle a wide range of transformations across different languages.

### Key Features

* Transform entire columns using simple AI prompts.
* User data never leaves the browser — values are *not* sent to any external AI models.
* Fix validation issues quickly by modifying problematic fields in bulk.
* Users can modify or delete rows, but cannot add new rows using AI.
* Supports prompts in all languages available in the importer.
* Users can preview the data before applying changes.


# Import Analytics

Get visibility into how users interact with CSVbox importer—from opening the widget to completing a successful import. Identify drop-offs, understand errors, and improve your data import experience.

{% hint style="info" %}
**Note:** This feature is available on **Pro plans and above**.
{% endhint %}

### What is Import Analytics?

Import Analytics tracks each importer session and shows how users move through the import flow:

* File upload
* Header selection
* Column mapping
* Data validation
* Final submission

You can use this data to understand where users struggle and optimize your importer configuration.

### What can you track?

#### 1. Import Funnel

See how many users reach each step of the import process:

* Importer Opened
* File Selected
* File Parsed
* Header Selected
* Mapping Completed
* Validation Completed
* Submit Succeeded

This helps you quickly identify where users drop off.

#### 2. Completion Rate

Measure how many users successfully complete the import compared to those who start it.

#### 3. Drop-offs

Understand where users abandon the process:

* Before selecting a file
* During header selection
* During mapping
* After validation

This helps pinpoint friction points in your flow.

#### 4. Time Taken per Step

Analyze how long users spend on each step:

* Time to select file
* Time to select header
* Time to complete mapping
* Time for validation
* Total time to complete import

#### 5. Error Insights

Identify the most common issues users face:

* File parsing errors
* Header selection issues
* Mapping errors
* Validation failures
* Destination/API errors

#### 6. Session Logs

View individual import sessions with details such as:

* Start time
* Final outcome (success, failed, incomplete)
* Last step reached
* Number of rows processed
* Error counts
* Total time taken

This is useful for debugging specific user issues.

### How to access Import Analytics

1. Go to your CSVbox Dashboard
2. Select your Sheet
3. Open the **Analytics** tab

You can filter data by date range to analyze recent or historical imports.

### Privacy & Data Handling

Import Analytics is designed to be privacy-safe.

Analytics **does not store**:

* Uploaded files
* Row data or cell values

It only tracks:

* Step-level events
* Counts and durations
* Error categories and codes

### How to use Import Analytics effectively

#### Improve onboarding

If many users drop off early, consider:

* Simplifying instructions
* Providing sample files
* Improving file format guidance

#### Optimize header selection

If users spend too long selecting headers:

* Ensure clean file templates
* Improve header row detection
* Add instructions for users

#### Fix mapping issues

If mapping drop-offs are high:

* Use clearer column names
* Reduce required fields where possible
* Enable better defaults

#### Reduce validation failures

If validation errors are common:

* Review validation rules
* Provide better error messages
* Offer clearer data requirements

#### Debug failed imports

Use session logs to:

* Inspect failed submissions
* Identify API/destination issues
* Reproduce and fix problems quickly

### Notes

* Analytics data is aggregated per sheet
* Sessions are automatically tracked—no setup required
* Abandoned sessions are detected after a period of inactivity

Import Analytics helps you move from guessing to understanding—so you can continuously improve your data import experience.


# AI Document Import

Extract structured tables from PDFs, images, and documents with simple, transparent pricing

CSVBox allows you to import tabular data from PDFs, images, and documents using AI-powered extraction. This feature is designed to make unstructured data import-ready in just a few steps while keeping pricing predictable and usage fully transparent.

***

### How to enable AI Document Import

You can enable this feature directly within your sheet configuration:

**Sheet Settings → Import Steps → File Upload → Enable AI Document Import**

Once enabled, users will be able to upload documents (PDFs, images, DOCX) during the import flow.

***

### Data privacy & security

Your documents are processed using secure external AI services for table extraction.

* Your uploaded documents are **not used to train or improve any public AI models**
* The service guarantees your data is **not used for any purpose other than processing your request**
* Files are handled securely and processed only for extraction

***

### What is a “document page”?

A **page** is the unit used for billing document processing.

* PDF → each page = 1 page
* Images (JPG, PNG, WEBP, etc.) → each image = 1 page
* DOCX → processed as pages based on document layout

Note: DOCX page counts are estimated based on document structure.

***

### Monthly included pages

Each plan includes a fixed number of document pages per billing cycle:

| Plan    | Included pages / month |
| ------- | ---------------------- |
| Sandbox | 5 pages                |
| Startup | 50 pages               |
| Pro     | 500 pages              |
| Growth  | 1,000 pages            |
| Plus    | 3,000 pages            |

* Limits reset every billing cycle
* Unused pages do not roll over

***

### Extra pages (credits)

If you exceed your monthly included pages:

* Additional pages cost **$0.03 per page**
* Charges are deducted from your prepaid credit balance

***

### What are credits?

Credits are a prepaid balance used for document processing.

* You can top up anytime via the **Plans page**
* Credits are used only after your included pages are exhausted
* Credits are stored as a monetary balance (USD)

Example:

* $10 ≈ 333 pages
* $25 ≈ 833 pages

***

### How usage is calculated

When you process documents, pages are consumed in this order:

1. Included monthly pages
2. Credit balance
3. If both are exhausted → processing is blocked

***

### Selecting the correct table

Many documents contain multiple tables. CSVBox gives you full control:

* All detected tables are shown in a preview
* You can review each table visually
* You select the exact table you want to import

This ensures accurate imports even from complex or multi-section documents.

***

### Example usage

You are on the Pro plan (500 pages/month)

You process 600 pages in a month:

* 500 pages → included
* 100 pages → credits

Cost:

* 100 × $0.03 = **$3.00**

***

### When will I be charged?

A page is charged when it is successfully sent for AI processing.

You are not charged when:

* file upload fails
* file is unsupported
* document cannot be read before processing starts

***

### Failed or low-quality results

In some cases:

* no tables are detected
* extraction quality is not usable

These pages are still charged because AI processing has already been performed.

***

### Partial processing

If processing stops midway:

* only successfully processed pages are charged

Example:

* 20-page document
* 12 pages processed before failure
* you are charged for 12 pages only

***

### Retries

* Manual retry → charged again
* System retry (internal failure) → not double-charged

***

### What happens when I run out of pages?

* You will receive an **email alert when you reach 90% of your usage**
* Once your included pages and credits are exhausted:
  * document processing will be blocked
  * you will be prompted to add credits

***

### Adding credits

You can add credits anytime from the **Plans page**:

1. Go to the Plans page
2. Choose a credit amount
3. Complete payment

Credits are applied instantly after successful payment.

***

### Viewing your usage

You can monitor usage from your dashboard:

* Included pages used and remaining
* Credit balance
* Pages processed this cycle
* Recent document processing activity

***

### Billing cycle

* Included pages reset every billing cycle
* Your billing cycle depends on your subscription start date
* Credit balance does not reset and carries forward

***

### Important notes

* Charges are based on pages processed, not successful extraction
* Each image counts as one page
* Credits are used only after included pages are exhausted
* CSV and Excel imports are not affected by this pricing

***

### FAQ

**Do unused pages roll over?**\
No, included pages reset every billing cycle.

**Do credits expire?**\
No, credits remain in your account until used.

**Can I get a refund for failed extraction?**\
If a failure is caused by a system issue, contact support and we’ll review it.

**Are CSV/Excel imports charged?**\
No, only document-based imports (PDF, images, DOCX) are counted.

**Can I increase my included pages?**\
Yes, upgrade your plan to get a higher monthly allowance.


# Document Metadata Fields

Document Metadata Fields let a sheet capture details straight from an uploaded file itself, instead of asking the person uploading to type them in. A field like `Author`, `Invoice Number`, or a fixed label can be filled in automatically the moment a file is uploaded, and it shows up alongside the rest of the imported data as a regular column.

Up to 20 of these can be configured per sheet, each sourced a different way depending on what kind of information is needed. Configuration is stored on `Sheet.pdf_metadata_config`; extraction is handled by `App\Services\PdfMetadataExtractor`, called from `AnalyzeController`.

### Why Use Document Metadata Fields

* **Invoices & receipts** — capture the invoice number or vendor name straight from the document, instead of asking the uploader to re-type it into a form field.
* **Applications & forms** — tag every submission with who authored the file or what it's titled, useful for sorting and follow-up later.
* **Batch labeling** — stamp every import from a particular source or partner with a fixed label, so downstream reports can filter by it.

#### Field Sources

Each Document Metadata Field is filled in one of three ways:

**Static Value**

A fixed value typed in once. It applies to every upload processed through this sheet — useful for labels and tags that never change.

*Example: `Batch Label` = "Q3 Vendor Import"*

**Native Document Property**

Most documents quietly carry their own properties, such as who created them or what they're titled. This source reads that value straight from the file.

*Example: `Author` = whoever created the PDF or Word document*

**Document Key-Value (Smart Match)**

Give the field a name to look for — like `Invoice Number` — and CSVbox finds the value printed next to that label on the page.

*Example: field name `Invoice Number` finds "INV-4471" on the page*

### Supported File Types

| File Type               | Static Value | Native Document Property | Document Key-Value |
| ----------------------- | ------------ | ------------------------ | ------------------ |
| PDF                     | Yes          | Yes                      | Yes                |
| Word document (`.docx`) | Yes          | Yes                      | Yes                |
| Image (`.jpg`, `.png`)  | Yes          | —                        | Yes                |

> **Note:** If a value can't be found for a field, it is simply left blank for that upload. This never blocks or fails the import — the rest of the file still comes through normally.

### Enabling Document Metadata Fields

1. Open the sheet to configure and go to `Import Steps` > `File Upload`.
2. Scroll to the **Document Metadata Fields** section.
3. Add a field and give it a name — this becomes the column name people will see once data is imported.
4. Choose how it should be filled in: **Static Value**, **Native Document Property**, or **Document Key-Value**, and provide whatever it needs (a value to use, or a label to look for).
5. Repeat for each detail to capture, then click **Save Metadata Fields**.

> **Note:** Up to 20 metadata fields are allowed per sheet, and each field name must be unique.

From that point on, every matching upload gets these fields filled in automatically — no extra steps for the person uploading.

#### Example

A sheet set up to receive vendor invoices, with three metadata fields configured, processing an upload named `invoice_4471.pdf`:

| Field            | Source                   | Value Captured   |
| ---------------- | ------------------------ | ---------------- |
| `Batch Label`    | Static Value             | Q3 Vendor Import |
| `Author`         | Native Document Property | J. Alvarez       |
| `Invoice Number` | Document Key-Value       | INV-4471         |

All three values are captured the moment the file is uploaded — nobody had to type any of them in. This happens automatically as part of processing the file, with no extra wait for the person uploading.


# Split Large CSV

Automatically split large CSV files into smaller parts for faster, more reliable imports with improved browser performance.

### Overview

Uploading very large CSV files can cause browser slowdowns, high memory usage, or upload timeouts — especially when files contain thousands of rows.

The **Split Large Files** feature solves this by automatically dividing large CSV files into smaller parts and processing them sequentially behind the scenes.

Each part:

* Preserves the original headers and column structure
* Reuses the same column mappings automatically
* Is processed independently for improved reliability
* Remains grouped under a single parent import for easier tracking

This makes large imports significantly more stable and easier to manage.

***

### When does file splitting activate?

File splitting activates only when **all** of the following conditions are met:

* The uploaded file is a **CSV** file\
  (`.xlsx` Excel files are not currently supported)
* **Split Large Files** is enabled for the sheet
* The uploaded file exceeds the configured **Rows per Import** limit

***

## Admin Configuration

The feature can be enabled per-sheet from:

**Sheet Settings → Import Steps**

***

### 1. Split Large Files

| Setting           | Options  | Default |
| ----------------- | -------- | ------- |
| Split Large Files | Yes / No | No      |

Enable this setting to automatically split large CSV uploads into smaller parts.

When disabled, the entire CSV file is processed as a single upload.

***

### 2. Rows per Import

| Setting         | Type   | Default |
| --------------- | ------ | ------- |
| Rows per Import | Number | 1000    |

Defines the maximum number of rows processed in each part.

#### Example

If a CSV contains **4,500 rows** and the limit is set to **1,000 rows**, CSVBox will process the file in:

* Part 1 → Rows 1–1000
* Part 2 → Rows 1001–2000
* Part 3 → Rows 2001–3000
* Part 4 → Rows 3001–4000
* Part 5 → Rows 4001–4500

#### Choosing the right value

| Smaller Parts                         | Larger Parts                                 |
| ------------------------------------- | -------------------------------------------- |
| Faster processing per upload          | Fewer total parts                            |
| Lower browser memory usage            | Higher memory usage                          |
| Better reliability for large datasets | Faster overall completion for moderate files |

For most use cases, **1,000–5,000 rows per part** works well.

***

### 3. User Confirmation Between Parts

| Setting           | Options                                    | Default |
| ----------------- | ------------------------------------------ | ------- |
| User Confirmation | No / Yes / No, only if there are no errors | No      |

Controls whether users must manually continue between parts.

#### Available Modes

| Mode                            | Behavior                                                                |
| ------------------------------- | ----------------------------------------------------------------------- |
| No                              | All parts upload automatically                                          |
| Yes                             | User must confirm before each next part uploads                         |
| No, only if there are no errors | Upload continues automatically unless a part contains validation errors |

For the smoothest experience, we recommend using **No**.

***

## End User Experience

### Step 1 — Upload a CSV File

The user uploads a CSV file normally through the importer.

If the file exceeds the configured row limit, CSVBox automatically prepares the file for multipart processing.

***

### Step 2 — Split Notification

Before processing begins, the user sees a notification such as:

> Large file detected\
> We'll split this file into smaller parts (X rows each) for smoother processing. Each part will be uploaded automatically, and your column mappings will be reused.

The user can then:

* **Start Upload** — Continue processing
* **Cancel** — Cancel the upload

***

### Step 3 — Map Columns (First Part Only)

The first part behaves exactly like a normal CSVBox import.

The user:

* Maps columns
* Reviews data
* Fixes validation issues if needed
* Submits the import

CSVBox automatically reuses the same mappings for all remaining parts.

The user does **not** need to map columns again.

***

### Step 4 — Automatic Processing of Remaining Parts

After the first part is submitted, remaining parts are processed automatically.

A progress indicator shows overall upload progress.

#### Example

```
Processing (2/5)

2,000 / 5,000 rows processed
```

Depending on the confirmation mode, the user may or may not be prompted between parts.

***

### Step 5 — Import Completion

Once all parts finish processing:

* The multipart upload is marked as completed
* All parts remain grouped under a single parent import
* Users can review part-level details if needed

***

## Viewing Multipart Imports

Multipart uploads appear as a single grouped import in the **All Imports** page.

CSVBox automatically tracks:

* Overall import status
* Number of processed parts
* Row counts
* Failed or partial parts

***

### Multipart Import Statuses

| Status          | Meaning                          |
| --------------- | -------------------------------- |
| Success         | All parts processed successfully |
| Processing      | Upload is still running          |
| Partial Failure | Some parts failed                |
| Failed          | Entire upload failed             |

***

### Viewing Part Details

Opening the import details shows information for each individual part.

| Column    | Description                     |
| --------- | ------------------------------- |
| Part      | Part sequence number            |
| Status    | Success / Failed / Processing   |
| Rows      | Rows processed in that part     |
| Import ID | Individual import reference     |
| Errors    | Validation or processing errors |

***

## Import Limits & Billing

Multipart uploads count as **a single import** toward your plan limits.

For example:

* A 10-part upload = **1 import**
* A 50-part upload = **1 import**

Individual parts are not counted separately.

***

## Reliability & Error Handling

Each part is processed independently for improved stability.

This means:

* A failure in one part does not invalidate already completed parts
* Failed parts can be retried individually
* Successfully processed parts remain intact

***

### If a Part Fails

If a part encounters validation errors or processing failures:

* The multipart upload is marked as **Partial Failure**
* Error details are available for the failed part
* Successfully processed parts remain unaffected

***

### If the Browser is Closed Mid-Upload

If the browser closes before processing completes:

* Completed parts remain saved
* Remaining parts stop processing
* The upload status reflects the completed progress

We recommend keeping the browser open until the upload finishes.

***

## Limitations

### Excel Files Are Not Supported

Currently, multipart processing supports **CSV files only**.

Excel (`.xlsx`) files are processed as a single upload regardless of size.

If needed, convert Excel files to CSV before uploading.

***

### Maximum Parts Per Upload

To ensure reliable processing, a single upload can generate up to **100 parts**.

If the configured row limit would exceed this number, the upload will stop with an error message.

In this case:

* Increase the **Rows per Import** value
* Or split the source CSV manually before uploading

***

## Best Practices

For optimal performance:

* Use CSV files instead of Excel for very large datasets
* Keep row limits between **1,000–5,000**
* Enable automatic uploads when possible
* Use smaller part sizes for slower devices or lower-memory environments

***

## FAQ

### Do all parts use the same column mapping?

Yes. Once the first part is mapped, CSVBox automatically reuses the same mapping for all remaining parts.

### Are parts sent separately to destinations?

Yes. Each part is processed independently and sent separately to the configured destination.

### Can users retry failed parts?

Yes. Failed parts can be retried individually without re-uploading the full file.

### Can I disable this feature for specific users?

No. The feature is configured per sheet, not per user.

If different users require different behavior, create separate sheets with different settings.


# AI Function Generator

Generate validation rules, transforms, virtual columns, and regex patterns using plain-language instructions

The **AI Function Generator** in CSVBox helps you create JavaScript functions and regex patterns without writing code manually. Simply describe what you want in plain English, and CSVBox will generate compatible logic for your importer automatically.

AI generation is available across:

* Validation Functions
* Virtual Columns
* Data Transforms
* Regex Column Type

This feature is designed to help you build advanced import logic faster, reduce manual coding, and simplify complex validation or transformation workflows.

***

## How It Works

1. Open a supported editor in CSVBox
2. Click the **Generate** button ✨
3. Describe the logic you want
4. CSVBox generates compatible JavaScript or regex code
5. Review the generated output
6. Insert and save the function

All generated code remains fully editable before saving.

***

## Supported Features

| Feature              | Generates                        |
| -------------------- | -------------------------------- |
| Validation Functions | JavaScript validation logic      |
| Virtual Columns      | JavaScript computed column logic |
| Data Transforms      | JavaScript transformation logic  |
| Regex Column Type    | Regular expression patterns      |

***

## Writing Better Prompts

The quality of the generated output depends heavily on how clearly the requirement is described.

For best results:

* Mention exact column names when possible
* Clearly describe the expected output
* Include formatting requirements
* Add examples for complex logic
* Focus on one task at a time
* Avoid vague instructions

***

## AI Generator for Validation Functions

Use AI to generate validation rules using plain-language descriptions instead of manually writing JavaScript validation logic.

Perfect for:

* Email validation
* Phone number validation
* Date validation
* Required field checks
* Cross-column validation
* Business-specific validation rules

***

### Example Prompts

#### Email Validation

**Prompt**

```
Validate that email addresses contain a valid @ symbol and domain
```

**Expected Behaviour**

* `"user@example.com"` → valid
* `"user@"` → invalid

***

#### Phone Number Validation

**Prompt**

```
Ensure phone numbers contain exactly 10 digits after removing special characters
```

**Expected Behaviour**

* `"(555) 123-4567"` → valid
* `"123-456"` → invalid

***

#### Date Validation

**Prompt**

```
Check that dates are in YYYY-MM-DD format and are not future dates
```

**Expected Behaviour**

* `"2023-12-25"` → valid
* `"12/25/2023"` → invalid

***

#### Cross Column Validation

**Prompt**

```
Ensure end_date is greater than start_date
```

**Expected Behaviour**

* `start_date="2023-01-01", end_date="2023-12-31"` → valid
* `start_date="2023-12-31", end_date="2023-01-01"` → invalid

***

### Validation Prompt Tips

For better validation results:

1. Start with action words such as:
   * validate
   * ensure
   * verify
   * require
   * check
2. Mention exact field names
3. Describe the validation condition clearly
4. Include valid and invalid examples for complex rules
5. Mention how blank or null values should behave

***

## AI Generator for Virtual Columns

Generate computed or derived columns using natural-language instructions.

Instead of manually writing JavaScript logic, describe what the virtual column should calculate or return.

Perfect for:

* Combining fields
* Calculated totals
* Derived statuses
* Formatting dates
* Extracting values
* Building custom labels

***

### Example Prompts

#### Full Name Column

**Prompt**

```
Combine first_name and last_name columns with proper capitalization
```

**Expected Behaviour**

* `"john"` + `"DOE"` → `"John Doe"`

***

#### Total Price Calculation

**Prompt**

```
Multiply quantity and unit_price columns to create total_price
```

**Expected Behaviour**

* `quantity=5, unit_price=10.50` → `52.50`

***

#### Date Formatting

**Prompt**

```
Convert dates from MM/DD/YYYY to Month DD, YYYY format
```

**Expected Behaviour**

* `"12/25/2023"` → `"December 25, 2023"`

***

#### Status Generation

**Prompt**

```
Create a status column based on due_date
```

**Expected Behaviour**

* Past date → `"Overdue"`
* Within 7 days → `"Due Soon"`
* Future date → `"On Track"`

***

### Virtual Column Prompt Tips

1. Mention which fields should be used
2. Describe the expected output clearly
3. Specify formatting rules
4. Include examples for calculated logic
5. Focus on one generated field at a time

***

## AI Generator for Data Transforms

Generate data transformation logic using plain-language instructions.

Use AI to clean, normalize, or reformat imported data automatically.

Perfect for:

* Text normalization
* Formatting cleanup
* Phone number cleanup
* Date conversion
* Value mapping
* Row transformations

***

### Example Prompts

#### Email Standardization

**Prompt**

```
Convert email addresses to lowercase and remove leading or trailing spaces
```

**Expected Behaviour**

* `" John.Doe@Example.COM "` → `"john.doe@example.com"`

***

#### Phone Number Cleanup

**Prompt**

```
Remove all non-numeric characters from phone numbers
```

**Expected Behaviour**

* `"(555) 123-4567"` → `"5551234567"`

***

#### Full Name Generation

**Prompt**

```
Combine first_name and last_name columns with proper capitalization
```

**Expected Behaviour**

* `"john"` + `"DOE"` → `"John Doe"`

***

#### Date Formatting

**Prompt**

```
Convert dates from MM/DD/YYYY format to YYYY-MM-DD
```

**Expected Behaviour**

* `"12/25/2023"` → `"2023-12-25"`

***

### Data Transform Prompt Tips

1. Start with action words like:
   * convert
   * remove
   * replace
   * format
   * normalize
   * combine
2. Specify the target column
3. Clearly describe the final output format
4. Include sample input/output values for complex transforms
5. Keep transformations focused and specific

***

## AI Generator for Regex Patterns

Generate regular expression (regex) patterns using plain-language descriptions.

Instead of manually writing regex syntax, describe the format you want to validate and CSVBox will generate the regex pattern automatically.

Perfect for:

* Email validation
* Phone number formats
* ZIP/postal codes
* Product code validation
* Password rules
* Custom formatting patterns

***

### Example Prompts

#### Phone Number Validation

**Prompt**

```
exactly 10 digits, no spaces or dashes
```

**Generated Pattern**

```regex
/^[\d]{10}$/
```

***

#### Email Validation

**Prompt**

```
valid email format with @ symbol and domain
```

**Generated Pattern**

```regex
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
```

***

#### ZIP Code Validation

**Prompt**

```
5 digit US zip code, optionally followed by hyphen and 4 digits
```

**Generated Pattern**

```regex
/^\d{5}(-\d{4})?$/
```

***

#### Product Code Format

**Prompt**

```
product code starting with 2 uppercase letters followed by exactly 4 digits
```

**Generated Pattern**

```regex
/^[A-Z]{2}\d{4}$/
```

***

## Best Practices

### Keep Prompts Focused

Short, focused prompts produce better results than large multi-step instructions.

Good:

```
Convert SKU values to uppercase
```

Less Effective:

```
Convert SKU values to uppercase, remove spaces, validate duplicates, generate category names, and create status values
```

***

### Use Exact Column Names

Prefer:

```
Combine first_name and last_name
```

Instead of:

```
Combine the user's names
```

***

### Mention Formatting Rules

Examples:

* `YYYY-MM-DD`
* `10 digit phone number`
* `uppercase product code`
* `currency with 2 decimals`

***

### Include Examples for Complex Logic

Adding examples improves accuracy significantly.

Example:

```
Convert dates from MM/DD/YYYY to YYYY-MM-DD. Example: 12/25/2023 → 2023-12-25
```

***

## Testing Generated Code

Always review and test generated code before saving.

Recommended testing steps:

1. Review the generated output
2. Test with sample values
3. Test blank and null values
4. Verify edge cases
5. Confirm expected formatting
6. Ensure only intended fields are modified

***

## Limitations

### Current Limitations

* Generates JavaScript functions and regex patterns only
* External APIs and network requests are not supported
* Browser APIs like localStorage and cookies cannot be used
* Complex multi-step workflows may require multiple functions

***

### Unsupported Operations

The AI cannot:

* Add or remove rows
* Modify your importer schema
* Access external systems
* Call third-party APIs
* Access databases
* Read files outside CSVBox
* Perform actions outside the CSVBox execution environment

***

## Common Issues

### Generation Failed

#### Possible Causes

* Network issue
* Temporary server-side issue
* Prompt too complex
* Prompt too ambiguous

#### Solutions

* Retry after a few seconds
* Simplify the prompt
* Make instructions more specific
* Break large logic into smaller prompts

***

### Generated Output Is Incorrect

#### Possible Causes

* Missing formatting details
* Ambiguous instructions
* Missing edge cases
* Unclear expected output

#### Solutions

* Add input/output examples
* Mention formatting rules explicitly
* Specify null or blank value handling
* Focus on one task at a time

***

## Tips for Better Results

1. Use exact field names whenever possible
2. Keep prompts concise and specific
3. Mention edge cases such as blank values
4. Include examples for complex rules
5. Generate one function at a time
6. Review generated code before saving
7. Test thoroughly before production use

***

## Security and Safety

Generated code runs within the CSVBox execution environment and follows CSVBox security restrictions.

Generated functions cannot:

* access external systems
* make network requests
* access browser storage
* modify importer configuration automatically

All generated code should be reviewed and tested before production use.


# Advanced Installation

Description of additional importer options and features.

1. [Dynamic Columns](/advanced-installation/dynamic-columns) - Add new columns to the template at run-time.
2. [Virtual Columns](/advanced-installation/virtual-columns) - Create new columns by applying custom data transformation logic.
3. [Unmapped Columns](/advanced-installation/unmapped-columns) - Allow users to submit columns not included in the sheet template.
4. [Ignored Columns](/advanced-installation/ignored-columns) - Option for users to drop columns from the import process.
5. [Import Links](/advanced-installation/import-links) - Accept files from your users without a website or an app.
6. [Validation Functions](/advanced-installation/validation-functions) - Implement advance validations using Javascript functions.
7. [Server Side Validation](/advanced-installation/server-side-validation) - Validate data at your server, report back errors for correction & re-upload.
8. [Data Transforms](/advanced-installation/data-transforms) - Bulk edit the dataset before pushing it to your system.
9. [REST File API](/advanced-installation/import-links) - Automate CSV submissions via API.
10. [Environment Variables](/advanced-installation/environment-variables) - Variables to configure the importer environment.
11. [Auth API](/advanced-installation/auth-api) - Authenticate API requests to CSVBox using a secure header-based scheme.
12. [Sheet API ](/advanced-installation/sheet-api)


# Dynamic Columns

Add new columns to the template at run-time.

Consider a scenario where you cannot have a fixed template for collecting data. The columns in the data model depend on the end user's preferences and/or some other criteria.&#x20;

For such cases, csvbox provides the flexibility to add unique **dynamic columns** for each import at run-time.

### Basic Installation

You can configure **dynamic columns** via the installation code.

Here's a basic configuration that adds 2 dynamic columns **qualification** and **experience**.&#x20;

{% tabs %}
{% tab title="Javascript" %}
Pass the dynamic columns that you want to add as an array input parameter to th&#x65;**`setDynamicColumns()`**&#x6D;ethod while initializing the importer.

```javascript
importer.setDynamicColumns([
         {
          "column_name" : "qualification"        
         },
         {
          "column_name": "experience"         
          }
])
```

Basic installation steps are available [here](https://help.csvbox.io/advanced-installation/pages/-MOGv6-aW2aB2fgDl2Eo#2.-install-code).
{% endtab %}

{% tab title="React" %}
Pass the dynamic columns as an object to the **`dynamicColumns`**&#x70;roperty of the **`CSVBoxButton`** component.&#x20;

```javascript
  dynamicColumns={[
               {
                 "column_name" : "qualification"        
               },
               {
                  "column_name": "experience"         
              }
  ]}
```

Basic installation steps are available [here](https://help.csvbox.io/advanced-installation/pages/-MOGv6-aW2aB2fgDl2Eo#2.-install-code).
{% endtab %}

{% tab title="Angular" %}
Add `[dynamicColumns]="dynamicColumns"` to the existing template.&#x20;

```
@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"
      [dynamicColumns]="dynamicColumns"
      [imported]="imported.bind(this)">
      Import
    </csvbox-button>
  `
})
```

Then pass the dynamic columns as an object to th&#x65;**`dynamicColumns`**&#x70;roperty of the AppComponent. Example:

```javascript
  dynamicColumns=[
               {
                 column_name : "qualification"        
               },
               {
                  column_name: "experience"         
              }
  ]
```

Basic installation steps are available [here](https://help.csvbox.io/advanced-installation/pages/-MOGv6-aW2aB2fgDl2Eo#2.-install-code).
{% endtab %}

{% tab title="Vuejs" %}
Add `:dynamicColumns="dynamicColumns"` to the existing template.&#x20;

```
<template>
  <div id="app">
    <CSVBoxButton 
      :licenseKey="licenseKey"
      :user="user"
      :dynamicColumns="dynamicColumns"      
      :onImport="onImport">
      Upload File
    </CSVBoxButton>
  </div>
</template>
```

Pass the dynamic columns as an object to th&#x65;**`dynamicColumns`**&#x70;roperty of th&#x65;**`CSVBoxButton`** component. Example:

```javascript
  dynamicColumns: [
               {
                 column_name : "qualification"        
               },
               {
                  column_name: "experience"         
              }
  ]
```

Basic installation steps are available [here](https://help.csvbox.io/advanced-installation/pages/-MOGv6-aW2aB2fgDl2Eo#2.-install-code).
{% endtab %}
{% endtabs %}

Dynamic columns will be visible in the importer along with the other regular columns.

<img src="/files/yZrGgxa4FQAIitYODbb2" alt="Dynamic Columns" data-size="original">

### **Advanced Configuration**

Dynamic columns can be configured by passing additiona&#x6C;**`<key>: <value>`**&#x70;airs to the array input parameter of th&#x65;**`setDynamicColumns()`**&#x6D;ethod.

Here is an example illustrating more configuration options.

{% tabs %}
{% tab title="Javascript" %}

```javascript
 importer.setDynamicColumns([
         {
          "column_name" : "qualification",
          "display_label": "Highest Qualification",
          "info_hint": "What is your highest educational degree",
          "matching_keywords": "degree, education",
          "type": "text",
          "validators": 
            {          	
              "min_length": 2,
              "max_length": 50
            },
          "default_value": "Masters",  
          "position": 2,
          "required": true
        },
        {
          "column_name": "experience",
          "display_label": "Work Experience",
          "info_hint": "Years of work experience",
          "matching_keywords": "",
          "type": "number",
          "validators": 
            {          	
              "min_value": 0,
              "max_value": 100
            },
          "position": 4,
          "required": false
        },
        {
          "column_name": "gender",
          "display_label": "Gender",
          "info_hint": "",
          "matching_keywords": "",
          "type": "list",
          "validators": 
            {          	
              "values": [
                              {"value": "m", "display_label": "male"},
                              {"value": "f", "display_label": "female"} 
                        ],
              "case_sensitive": false
            },
          "required": true
        }
])
```

{% endtab %}

{% tab title="React" %}

```javascript
  dynamicColumns={[
         {
          "column_name" : "qualification",
          "display_label": "Highest Qualification",
          "info_hint": "What is your highest educational degree",
          "matching_keywords": "degree, education",
          "type": "text",
          "validators": 
          {          	
            "min_length": 2,
            "max_length": 50
          },
          "default_value": "Masters",
          "position": 2,
          "required": true
    },
 {
          "column_name": "experience",
          "display_label": "Work Experience",
          "info_hint": "Years of work experience",
          "matching_keywords": "",
          "type": "number",
          "validators": 
          {          	
            "min_value": 0,
            "max_value": 100
          },
          "position": 4,
          "required": false
    },
    {
          "column_name": "gender",
          "display_label": "Gender",
          "info_hint": "",
          "matching_keywords": "",
          "type": "list",
          "validators": 
          {          	
            "values": [
                            {"value": "m", "display_label": "male"},
                            {"value": "f", "display_label": "female"} 
                      ],
            "case_sensitive": false
          },
          "required": true
    }
]}
```

{% endtab %}

{% tab title="Angular" %}

```javascript
dynamicColumns=[
    {
     column_name: "qualification",
     display_label: "Highest Qualification",
     info_hint: "What is your highest educational degree",
     matching_keywords: "degree, education",
     type: "text",
     validators: 
     {            
       min_length: 2,
       max_length: 50
     },
     default_value: "Masters",  
     position: 2,
     required: true
  },
  {
     column_name: "experience",
     display_label: "Work Experience",
     info_hint: "Years of work experience",
     matching_keywords: "",
     type: "number",
     validators: 
     {            
       min_value: 0,
       max_value: 100
     },
     position: 4,
     required: false
  },
  {
     column_name: "gender",
     display_label: "Gender",
     info_hint: "",
     matching_keywords: "",
     type: "list",
     validators: 
     {            
       values: [
                       {value: "m", display_label: "male"},
                       {value: "f", display_label: "female"} 
                 ],
       case_sensitive: false
     },
     required: true
  }
  ];
```

{% endtab %}

{% tab title="Vuejs" %}

```javascript
 dynamicColumns: [
  {
   column_name: "qualification",
   display_label: "Highest Qualification",
   info_hint: "What is your highest educational degree",
   matching_keywords: "degree, education",
   type: "text",
   validators: 
   {            
     min_length: 2,
     max_length: 50
   },
   default_value: "Masters",
   position: 2,
   required: true
},
{
   column_name: "experience",
   display_label: "Work Experience",
   info_hint: "Years of work experience",
   matching_keywords: "",
   type: "number",
   validators: 
   {            
     min_value: 0,
     max_value: 100
   },
   position: 4,
   required: false
},
{
   column_name: "gender",
   display_label: "Gender",
   info_hint: "",
   matching_keywords: "",
   type: "list",
   validators: 
   {            
     values: [
                     {value: "m", display_label: "male"},
                     {value: "f", display_label: "female"} 
               ],
     case_sensitive: false
   },
   required: true
}
]
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**column\_name** is the only key that is mandatory for adding a dynamic column.
{% endhint %}

#### Configuration Options

<table><thead><tr><th width="321.0208971718002">Key</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#column-name">column_name</a> <mark style="color:red;">*</mark></td><td>It is the column name that will be pushed to the data destination.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#display-label">display_label</a></td><td>The user-friendly column label that the users will see in the importer.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#info-hint">info_hint</a></td><td>Info Hints are help tooltips that will get displayed when the users hover the mouse over the Display Label (or click it) in the importer.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#matching-keywords">matching_keywords</a></td><td>Comma-separated set of keywords as alternative matching options to help users match column names automatically. </td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#column-type">type</a></td><td>It specifies the data type of the incoming data. Possible values are: <strong>text</strong>, <strong>number</strong>, <strong>email</strong>, <strong>date</strong>, <strong>boolean</strong>, <strong>regex</strong>, <strong>ip</strong>, <strong>url</strong>, <strong>credit_card</strong>, <strong>phone_number, currency, list, dependent_list, dynamic_list, dependent_dynamic_list, multiselect_list,</strong> and <strong>multiselect_dynamic_list.</strong></td></tr><tr><td><a href="#validator-options">validators</a></td><td>The validation rules for the data based on the column type. Validator options are mentioned below.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#default-value">default_value</a></td><td>A default filler value for the column in case the incoming data is blank.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#required">required</a></td><td>It indicates whether a column is mandatory.</td></tr><tr><td><a href="#column-position">position</a></td><td>It defines the display index of the column. Starts from 1.</td></tr><tr><td><a href="/pages/-MfXTzcp1nRANdG4Hwlq#read-only">read_only</a></td><td>If configured to <strong>true</strong> then the users will not be able to edit the data of this column. The default value is <strong>false</strong>.</td></tr></tbody></table>

#### Validator options

<table><thead><tr><th width="150">Column Type</th><th width="329.4">Validators</th><th>Example</th></tr></thead><tbody><tr><td>text</td><td><ol><li><strong>min_length</strong></li><li><strong>max_length</strong></li></ol></td><td><p>"min_length": 2,</p><p>"max_length": 50</p></td></tr><tr><td>number</td><td><ol><li><strong>min_value</strong></li><li><strong>max_value</strong></li><li><strong>number_type</strong><br><strong>-</strong> Valid values: "any", "integer"<br>- Default: "any" </li><li><strong>allow_commas</strong><br><strong>-</strong> Valid values: true, false<br>- Default: false</li><li><strong>excel_value</strong><br>- Valid values: "raw", "formatted"<br>- Default: "formatted" </li></ol></td><td><p>"min_value": -2,</p><p>"max_value": 100,<br>"number_type": "integer",<br>"allow_commas": true,<br>"excel_value": "raw"</p></td></tr><tr><td>email</td><td>-</td><td></td></tr><tr><td>date</td><td><ol><li><strong>format</strong></li></ol></td><td>"format": ["MM/DD/YYYY", "MM.DD.YYYY", "MM-DD-YYYY"]</td></tr><tr><td>time</td><td><ol><li><strong>format</strong></li></ol></td><td>"format": ["<strong>h:mm:ss</strong>","<strong>hh:mm:ss</strong>"]</td></tr><tr><td>boolean</td><td>-</td><td></td></tr><tr><td>regex</td><td><ol><li><strong>expression</strong></li><li><strong>error_message</strong></li></ol></td><td><p>"expression": "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",<br>"error_message": "Invalid format"</p><p></p><p><em>Note: The special characters in the expression need to be escaped. You may use a tool like</em> <a href="https://www.freeformatter.com/javascript-escape.html#ad-output"><em>this</em> </a><em>for escaping.</em></p></td></tr><tr><td>ip</td><td><ol><li><strong>version</strong></li></ol></td><td>"version": “ipv4”</td></tr><tr><td>url</td><td>-</td><td></td></tr><tr><td>credit_card</td><td>-</td><td></td></tr><tr><td>phone_number</td><td><ol><li><strong>country_code</strong></li></ol></td><td>"country_code": "de"</td></tr><tr><td>currency</td><td><ol><li><strong>symbol</strong></li><li><strong>require_symbol</strong></li><li><strong>allow_space_after_symbol</strong></li><li><strong>symbol_after_digits</strong></li><li><strong>allow_negatives</strong></li><li><strong>parens_for_negatives</strong></li><li><strong>negative_sign_before_digits</strong></li><li><strong>negative_sign_after_digits</strong></li><li><strong>thousands_separato</strong></li><li><strong>decimal_separator</strong></li><li><strong>allow_decimal</strong></li><li><strong>require_decimal</strong></li><li><strong>digits_after_decima</strong></li><li><strong>allow_space_after_digits</strong></li></ol><p><strong>Note:</strong> The array <code>digits_after_decimal</code> is filled with the exact number of digits allowed not a range, for example a range 1 to 3 will be given as [1, 2, 3].</p></td><td><p>"symbol": "$", </p><p>"require_symbol": false,</p><p>"allow_space_after_symbol": false, "symbol_after_digits": false, "allow_negatives": true, "parens_for_negatives": false, "negative_sign_before_digits": false,</p><p>"negative_sign_after_digits": false,</p><p>"thousands_separator": ",", "decimal_separator": ".", "allow_decimal": true, "require_decimal": false, "digits_after_decimal": 2, "allow_space_after_digits": false</p></td></tr><tr><td>list</td><td><ol><li><p><strong>values</strong></p><ul><li><strong>value</strong></li><li><strong>display_label</strong> (optional)</li><li><p><strong>dependents</strong> (optional)</p><ul><li><strong>value</strong></li><li><strong>display_label</strong></li></ul></li></ul></li><li><strong>case_sensitive</strong></li><li><strong>other_values</strong> (Optional. Default is false)</li><li><strong>accept_list_values</strong><br>(Optional. Default is false)</li></ol></td><td><p>"values": [ </p><p>{"value": "USA", "display_label": "USA", "dependents": [ {"value": "ny", "display_label": "New York"}, {"value": "ch", "display_label": "Chicago"}, {"value": "se", "display_label": "Seatle"}, {"value": "mi", "display_label": "Miami"} ]}, {"value": "Canada", "display_label": "Canada", "dependents": [ {"value": "to", "display_label": "Toronto"}, {"value": "va", "display_label": "Vancouver"}<br>]}</p><p>],</p><p>"case_sensitive": false,<br>"other_values": false,<br>"accept_list_values": true</p></td></tr><tr><td>dependent_list</td><td><ol><li><strong>primary_column</strong></li></ol></td><td>"primary_column": "countries"</td></tr><tr><td>dynamic_list</td><td><ol><li><strong>source_url</strong></li><li><strong>request_method</strong></li><li><strong>request_headers</strong></li><li><strong>custom_user_attributes</strong> (Optional. Default is true)</li><li><strong>other_values</strong> (Optional. Default is false)</li></ol></td><td><p>"source_url": "https://api.myapp.com/countries",<br>"request_method": "POST",</p><p>"request_headers": [</p><p>{"key": "Content-Type", "value": "application/json"},</p><p>{"key": "X-Access-Token", "value": "71ab1d73a4d1319b260e9a0sdbdbc1c"}</p><p>],<br>custom_user_attributes: true,<br>"other_values": false</p></td></tr><tr><td>dependent_dynamic_list</td><td><ol><li><strong>primary_column</strong></li></ol></td><td>"primary_column": "countries"</td></tr><tr><td>multiselect_list</td><td><ol><li><strong>values</strong></li><li><strong>delimiter</strong> (Optional. Default is comma ",")</li><li><strong>case_sensitive</strong></li><li><strong>other_values</strong> (Optional. Default is false)</li></ol></td><td><p>"values": ["Red", "Green", "Blue"],</p><p>"delimiter": ".",<br>"case_sensitive": false,<br>"other_values": false</p></td></tr><tr><td>multiselect_dynamic_list</td><td><ol><li><strong>source_url</strong></li><li><strong>request_method</strong></li><li><strong>request_headers</strong></li><li><strong>delimiter</strong> (Optional. Default is comma ",")</li><li><strong>other_values</strong> (Optional. Default is false)</li></ol></td><td><p>"source_url": "https://api.myapp.com/colors",<br>"request_method": "POST",</p><p>"request_headers": [</p><p>{"key": "Content-Type", "value": "application/json"},</p><p>{"key": "X-Access-Token", "value": "71ab1d73a4d1319b260e9a0sdbdbc1c"}</p><p>],<br>"delimiter": ".",<br>"other_values": false</p></td></tr></tbody></table>

#### **Column Position**

In general, the dynamic columns are displayed only after the regular columns.

The `position`parameter helps to re-order the position of the dynamic columns and get them displayed before or in between the regular columns.&#x20;

The `position` value starts from 1 indicating the first position in the final column list. It is an optional parameter while defining the dynamic columns.

### **Receiving Dynamic Data**

The data from the dynamic columns is available in the data destinations along with the data from the regular columns. Currently, dynamic columns are supported by the following destinations only:

1. [None](/destinations#none)
2. [API/Webhook](/destinations#webhook)
3. [Test API](/destinations#test-api)
4. [Amazon S3](/destinations#amazon-s3)

#### API/Webhook Data Destination

In the API response JSON object, the dynamic data will be displayed inside the **\_dynamic\_data** object as shown below. The **\_dynamic\_data** object will be visible only if the dynamic columns are configured for the import. In the example below check lines 12 and 31.

```json
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3",
          "_dynamic_data":{
		"qualification": "MBA",
		"experience": "3"
           }
    },
    "custom_fields": {
      "user_id": "1002"
    }
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "row_data":{
          "Name": "EPower Technology EP-600PM Power Supply 600W ATX12V 2.3 Single 120mm Cooling Fan Bare",
          "SKU": "AS-103824",
          "Price": "95.35",
          "Quantity": "8",
           "_dynamic_data":{
		"qualification": "MA",
		"experience": "6"
           }
        },
    "custom_fields": {
      "user_id": "1002"
    }
  },
]

```

#### S3 Data Destination

In the AWS S3 file store, the dynamic columns will be added as new columns in the uploaded file.


# Virtual Columns

Create new columns by applying custom data transformation logic.

**Virtual Columns** are one of CSVbox’s most powerful data transformation tools.&#x20;

You can run small snippets of JavaScript that can **merge**, **split**, **re-format**, **correct**, and **enrich** incoming data during a data import.

Super useful if you need to process your data, do calculations, merge things together, etc before receiving it at your end. The sky is the limit!

### How it works

1. For any sheet (template), add a Virtual Column via the CSVbox dashboard.
2. Attach a Javascript snippet to the Virtual Column.
3. After the users submit the CSV file, the Javascript will run to populate data in the Virtual Columns.
4. Data from the uploaded CSV as well as the Virtual Column will be pushed to the data destination.

#### Example

{% tabs %}
{% tab title="Merge" %}
Merge data from the *first\_name* and the *last\_name* columns into a new Virtual Column named *full\_name*.

Here is the attached Javascript for the *full\_name* Virtual Column:

```javascript
try
{  	
	return csvbox.row["first_name"] + ' ' + csvbox.row["last_name"];
}
catch(err)
{  
  	return 'error: ' + err.name + ' | ' + err.message;
}
```

{% endtab %}

{% tab title="Re-format" %}
Normalize the incoming *order\_date* into UTC format.

```javascript
try
{  	const date = new Date(csvbox.row["order_date"]);

	return date.toUTCString();
}
catch(err)
{  
  	return 'error: ' + err.name + ' | ' + err.message;
}
```

{% endtab %}

{% tab title="Static" %}
Pass any static value to the Virtual Column.

```javascript
try
{  	
	return 'Amazon';
}
catch(err)
{  
  	return 'error: ' + err.name + ' | ' + err.message;
}
```

{% endtab %}
{% endtabs %}

### Adding Virtual Columns

1. Go to the edit sheet page > **Columns** tab > Click **Add Virtual Column** button.
2. Add Virtual **Column Name**.
3. Provide **Javascript** code.
4. Attach **Dependent** libraries (optional).
5. Click **Save**.

<div align="left"><figure><img src="/files/lgxsG5QEKmtmdJPa1K7n" alt=""><figcaption><p>Add Virtual Column</p></figcaption></figure></div>

### Data Rows

Data rows are the core object that Virtual Columns are built around. For each incoming CSV data row, you can read its data and manipulate it via Javascript to add to the Virtual Column.

After the user submits the CSV, the Javascript will run for each data row in sequential order to populate Virtual Columns.

The `csvbox.row` object is available to work with each row. You can use `csvbox.row["column_name"]` to read the specific column of the data row. Utilize data from a single column, multiple columns, or no columns to process via Javascript and populate Virtual Columns.&#x20;

### Inserting Data into Virtual Columns

The value returned by the Javascript snippet is added to the Virtual Column.&#x20;

```javascript
// Extracting first name    
try
{  	var fullName = csvbox.row["full_name"].split(' ');

	// retruning value
	return  fullName[0];
}
catch(err)
{  
	//returning error
  	return 'error: ' + err.name + ' | ' + err.message;
}
```

{% hint style="info" %}
If Javascript returns a `NULL` value, an empty string will be added to the Virtual Column.
{% endhint %}

### Receiving Virtual Data

The Virtual Column data is available at all the data destinations along with the data from the regular columns.

{% tabs %}
{% tab title="API/Webhook, Zapier etc" %}
In the API response JSON object, the dynamic data will be displayed inside the **\_virtual\_data** object as shown below. The **\_virtual\_data** object will be visible only if the Virtual Columns are added to the sheet. In the example below check lines 12 and 31.

{% code lineNumbers="true" %}

```json
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3",
          "_virtual_data":{
		"SKU_prefix": "AS",
		"Compare_Price": "43.00"
           }
    },
    "custom_fields": {
      "user_id": "1002"
    }
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "row_data":{
          "Name": "EPower EP-600PM Power Supply Cooling Fan",
          "SKU": "SS-103824",
          "Price": "95.35",
          "Quantity": "8",
           "_virtual_data":{
		"SKU_prefix": "SS",
		"Compare_Price": "105.35"
           }
        },
    "custom_fields": {
      "user_id": "1002"
    }
  },
]
```

{% endcode %}
{% endtab %}

{% tab title="MySQL, Bubble, Airtable etc " %}
Virtual Columns will be available in the column mapping modal. You can push Virtual Columns to any receiving data field of your choice.

<div align="left"><figure><img src="/files/NX3QE0cJKcCA2nrhP8so" alt=""><figcaption><p>Mapping Virtual Columns</p></figcaption></figure></div>
{% endtab %}

{% tab title="S3, FTP etc" %}
In the AWS S3 file store, the Virtual Columns will be added as new columns in the uploaded file.
{% endtab %}
{% endtabs %}

The virtual data will also be available on the [client-side JSON object](/getting-started/3.-receive-data#data-at-the-client-side).

### Dependencies

In the Javascript snippet, you can utilize an external library that is hosted via a CDN. You can also call any external API endpoint to fetch real-time data. Simply add the library script tag and/or any custom scripts to the 'Dependencies' section and start using it in the main Javascript snippet.

For each import, the dependent scripts will be run only once. Whereas the main Javascript snippet will be executed once for each row.

<div align="left"><figure><img src="/files/E1Xr7GwOGqe9Tt1OIXMt" alt=""><figcaption><p>VIrtual Column Processing</p></figcaption></figure></div>

<details>

<summary>Example</summary>

Consider you want to use the data from the incoming **USD\_amount** column to create a new **GBP\_amount** Virtual Column. The currency amount should be converted from USD to GBP using real-time exchange rates.

We can utilize the [Money.js](https://github.com/openexchangerates/money.js) library for currency conversions and [Open Exchange Rates ](https://openexchangerates.org/)for fetching real-time conversion rates. Here is the sample dependency code:

{% code title="Dependent Scripts" %}

```javascript
//Load jQuery via CDN
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"> </script>

//Load Money.js via CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/money.js/0.0.1/money.min.js" integrity="sha512-dfZQaBBlTXvL+AUQKi7dd/9kb/KhreymnZYVdinjigqTdZUrAnLUNJRV34DUPFCdyek9mMBns3rzTlipnBKhTg==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script>

//Fetch real-time conversion rates via ajax call and initialize fx object
<script type="text/javascript" >

    // Load exchange rates data via AJAX:
    $.getJSON(
        // NB: using Open Exchange Rates here, but you can use any source!
        'https://openexchangerates.org/api/latest.json?app_id=fa4744d77e844adb9cc3533b7ae162f3',
        function(data) {
            // Check money.js has finished loading:
            if (typeof fx !== "undefined" && fx.rates) {
                fx.rates = data.rates;
                fx.base = data.base;
            } else {
                // If not, apply to fxSetup global:
                var fxSetup = {
                    rates: data.rates,
                    base: data.base
                }
            }
        }
    );
</script>
```

{% endcode %}

Corresponding Javascript snippet for **GBP\_amount** Virtual Column:

{% code title="Javascript snippet" %}

```javascript
try
{  	
	//USD to GBP
	return fx.convert(csvbox.row["USD_amount"], {from: "USD", to: "GBP"});
	
}
catch(err)
{  
	//returning error
  	return 'error: ' + err.name + ' | ' + err.message;
}
```

{% endcode %}

</details>

### Variables

In the Virtual Column Javascript snippet, you have access to data variables included in the **`csvbox`** object. The following data is available:

#### `csvbox.row`

It contains row data. Each cell in the row can be accessed by providing the column name. Examples:

```javascript
csvbox.row["first_name"]
csvbox.row["order_id"]
csvbox.row["price"]
```

{% hint style="warning" %}
The column name must exist on the sheet or an error will be thrown.
{% endhint %}

#### `csvbox.user`

It contains the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) defined while initializing the importer. Examples:

```javascript
csvbox.user["user_id"]
csvbox.user["team_id"]
csvbox.user["isAuthenticated"]
```

#### `csvbox.import`

This refers to the current import-specific data. The following data is available:

```javascript
csvbox.import["sheet_id"]
csvbox.import["sheet_name"]
csvbox.import["original_filename"]
csvbox.import["import_start_time"]
csvbox.import["destination_type"]
csvbox.import["total_rows"]
csvbox.import["row_number"] //current row number starting with 1
```

#### `csvbox.virtual`

It contains data from the preceeding virtual columns.

```javascript
csvbox.virtual["final_name"]
csvbox.virtual["age"]
```

#### `csvbox.environment`

It contains the [environment variables](/advanced-installation/environment-variables) that are passed during importer initialization.

```javascript
// Example Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

{% hint style="warning" %}
Data from only the virtual columns that are defined before the current virtual column are available for use.
{% endhint %}

{% hint style="info" %}
&#x20;***console.log(csvbox);***&#x20;

With this statement, you can print all the available variables in the debugging console,
{% endhint %}


# Unmapped Columns

Allow users to submit columns not included in the sheet template.

There can be cases where each of your users has unique columns in their sheets that they want to upload. These columns are not known to you beforehand and hence cannot be configured as part of the sheet template. These columns can be discovered only after the user uploads the file. Hence, they cannot be configured as [dynamic columns](/advanced-installation/dynamic-columns#basic-installation) either. We call these columns **Unmapped Columns**.

With CSVbox you now have the option to accept Unmapped Columns as well.&#x20;

### Enabling Unmapped Columns

You can start accepting data from the Unmapped Columns by simply activating the option via your CSVbox dashboard.

1. Login to your account.
2. Edit the sheet of your choice.
3. Go to the '**Options**' Tab.
4. Select '**Yes**' for the '**Accept Unmapped Columns?**' field.
5. Click '**Save**'.

The importer will then start accepting data from the Unmapped Columns submitted by the users.

### **Receiving Data from Unmapped Columns**

The data from the Unmapped Columns is available in the data destinations along with the data from the regular columns. Currently, Unmapped Columns are supported by the following destinations only:

1. [API/Webhook](/destinations#webhook)
2. [Amazon S3](/destinations#amazon-s3)

#### API/Webhook Data Destination

In the API response JSON object, the dynamic data will be displayed inside the **\_unmapped\_data** object as shown below. The **\_unmapped\_data** object will be visible only if the Unmapped Columns are activated. In the example below check lines 12 and 32.

```json
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3",
          "_unmapped_data":{
		"Barcode": "7832748937489",
		"_empty_header_1": "TP-Link",
		"Tags": "electronics, networking"
           }
    },
    "custom_fields": {
      "user_id": "1002"
    }
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "row_data":{
          "Name": "EPower Technology EP-600PM Power Supply 600W ATX12V 2.3 Single 120mm Cooling Fan Bare",
          "SKU": "AS-103824",
          "Price": "95.35",
          "Quantity": "8",
           "_unmapped_data":{
		"Barcode": "4532748937411",
		"_empty_header_1": "EPower",
		"Tags": ""
           }
        },
    "custom_fields": {
      "user_id": "1002"
    }
  },
]

```

{% hint style="info" %}
If any of the Unmapped Columns do not have a header specified in the user uploaded file, then the app will auto-insert column names  *\_empty\_header\_1*, *\_empty\_header\_2*, and so on.
{% endhint %}

#### S3 Data Destination

In the AWS S3 file store, the Unmapped Columns will be added as new columns in the uploaded file.


# Ignored Columns

Option for users to drop columns from the import process.

There can be a situation where users want to skip a column during data submission. This is especially useful when using the importer to update existing data. To avoid overwriting existing columns, the users can mark those columns as **Ignored** and the importer will skip them.

With CSVbox you can give the option to the users to mark certain columns as Ignored.&#x20;

### Enabling Ignored Columns

The first step is to activate the Ignore Columns option via your CSVbox dashboard.

1. Login to your account.
2. Edit the sheet of your choice.
3. Go to the '**Options**' Tab.
4. Select '**Yes**' for the '**Allow Columns to be Ignored**' field.
5. Click '**Save**'.

The importer will then show the option to mark the columns as Ignored on the Column Mapping screen.

<figure><img src="/files/hxKRQiRnpjDTOf9esKBG" alt=""><figcaption><p>Ignored Column Selection</p></figcaption></figure>

<figure><img src="/files/6SzSDAqu4WZKhaEXZ2g2" alt=""><figcaption><p>Ignored Column</p></figcaption></figure>

The columns that are marked as Ignored will not be visible on the Verify Data screen.

{% hint style="info" %}
For columns that are marked as "Required", the Ignore Column option will not be available for selection.
{% endhint %}

#### Default Selection

When the Default Selection option is configured to 'Yes', all the unmapped columns will default to Ignore Column.

### **Data from Ignored Columns**

The Ignored Columns will not be pushed to any destination. Only the metadata (List of Ignored Columns) will be pushed to the following destinations:

1. [API/Webhook](/destinations#webhook)
2. [Zapier](/destinations/zapier)
3. [Client Side](/getting-started/3.-receive-data#data-at-the-client-side)

#### Ignored Column List

In the API response JSON object, the list of Ignored Columns will be displayed inside the **ignored\_columns** object as shown below. The **ignored\_columns** object will be visible only if the Ignored Columns are activated. In the example below check lines 16 and 36.

{% code lineNumbers="true" %}

```json
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3"        
    },
    "custom_fields": {
      "user_id": "1002"
    },
    "ignored_columns": ["qualification", "experience"]
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "row_data":{
          "Name": "EPower Technology EP-600PM Power Supply 600W ATX12V 2.3 Single 120mm Cooling Fan Bare",
          "SKU": "AS-103824",
          "Price": "95.35",
          "Quantity": "8",
           "_ignored_data":{
		"qualification": "MA",
		"experience": "6"
           }
        },
    "custom_fields": {
      "user_id": "1002"
    },
    "ignored_columns": ["qualification", "experience"]
  }
]
```

{% endcode %}


# Import Links

No code import links to accept files anywhere.

Use Import Links to accept files from your users without a website or an app. Create an import page in just a few clicks and share the link with your users—no code required. It is an alternative to the import button that can be set up by adding the integration code.

After you have created a sheet go to the 'Code' tab on the Edit Sheet page. Below the integration code find the Import Link of the sheet. It will look something like this:

```javascript
https://app.csvbox.io/upload/2gzJa5YO3QPLYK6Bj7Qmq5bpbFqXno?user_id=default123
```

Simply share this link with your users to start collecting spreadsheets.

#### Referencing the user in the Import Links

You can configure the query parameters in the link to identify and match the users with their respective imports. Add up to 5 query parameters with custom user attributes that help you identify the users in your platform. The custom user attributes will be pushed to your destination along with the uploaded data.

**user\_id** is the only custom attribute that is mandatory. Apart from **user\_id,** you can add up to 4 custom attributes in the`&key=value`format. Example:

```javascript
https://app.csvbox.io/upload/2gzJa5YO3QPLYK6Bj7Qmq5bpbFqXno?user_id=1a2b3c4d5e6f&team_id=sales2&isAuthenticated=true&permissionLevel=admin&email=abc@example.com
```

#### Activating Import Links

You can activate or deactivate import links via sheet settings. Go to **Sheet Edit** page > **Options** tab > **Import Links** > Select *Activate* or *Deactivate in the* dropdown.

<div align="left"><figure><img src="/files/UZ3DY8mMwfQURIIV4un8" alt=""><figcaption><p>Activate Import Links</p></figcaption></figure></div>

If the Import Links are deactivated the users will see the following message:

<figure><img src="/files/va7xw5eRuXmxYTwo7ftK" alt=""><figcaption><p>Import Links disabled</p></figcaption></figure>


# Validation Functions

Validate data with custom Javascript functions.

If the validations you require are not covered with the in-built [data type validations](/dashboard-settings/validations) in CSVbox then you can code your own custom validation functions in Javascript.

There are two types of Validation Functions: 1. Row Functions 2. Column Functions.

**Row Functions** run validation on each row of data and return an error message (if any) for the user. These functions run at the beginning of the "validate" step and then also when a row data is updated during the "validate" step. An example use case for Row Function is if you want to mark a cell as "mandatory" based on a specific value in another cell in the same row.

**Column Functions** run validation on a set of selected columns when the user clicks the Submit button on the "validate" step. These are best used in cases where entire column data is required for validation. For example, say you want to find duplicate entries in a column. You could grab all the values in the column, find duplicate values, and display the message to the user.

<figure><img src="/files/fA5u5T6o8WKMfPdXV1xD" alt=""><figcaption><p>Validation Functions</p></figcaption></figure>

## Row Functions

### Adding Row Functions

* Go to the edit sheet page > **Columns** tab > Click **Add Functions** button.
* Add **Function Name**.
* Select **Row** under Function Type.
* Provide **Javascript** code.
* Click **Save**.

<figure><img src="/files/HnoYo2dZEHi7UN5C9vhi" alt=""><figcaption><p>Adding Row Functions</p></figcaption></figure>

### Example Row Functions

{% tabs %}
{% tab title="Dependent Columns" %}
Column 5 is mandatory only if the column 4 is not null.

```javascript
//replace "col4" and "col5" with actual column names

if(csvbox.row["col4"] != "" && csvbox.row["col5"] == "") {
  let err = [
  {   
    "column": "col5",
    "message": "Column 5 is mandatory if Column 4 is not empty"
  }];    
  return err;  
}
```

{% endtab %}

{% tab title="Mandatory Column Combination" %}
Either col 2 or col 3 needs to have data.

```javascript
//replace "col2" and "col3" with actual column names

if(csvbox.row["col2"] == "" && csvbox.row["col3"] == ""){
  let err = [
    {   
      "column": "col2",
      "message": "Columns 2 OR 3 needs to have data"
    },
    {
      "column": "col3",
      "message": "Columns 2 OR 3 needs to have data"
    }
  ];    
  return err;
}
```

{% endtab %}
{% endtabs %}

### Variables in the Row Function

You have access to data variables included in the **`csvbox`** object. The following data is available:

#### `csvbox.row`

It contains row data. Each cell in the row can be accessed by providing the column name. Examples:

```javascript
csvbox.row["first_name"]
csvbox.row["order_id"]
csvbox.row["price"]
```

{% hint style="warning" %}
The column name must exist on the sheet or an error will be thrown.
{% endhint %}

#### `csvbox.user`

It contains the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) defined while initializing the importer. Examples:

```javascript
csvbox.user["user_id"]
csvbox.user["team_id"]
csvbox.user["isAuthenticated"]
```

#### `csvbox.import`

This refers to the current import-specific data. The following data is available:

```javascript
csvbox.import["sheet_id"]
csvbox.import["sheet_name"]
csvbox.import["original_filename"]
csvbox.import["import_start_time"]
csvbox.import["destination_type"]
csvbox.import["total_rows"]
csvbox.import["row_number"] //current row number starting with 1
```

#### `csvbox.columns`

This object contains the column metadata (name, type).

```javascript
//Example usage
let column = csvbox.columns['birthdate'];

if(column.type == 'date') {

   // code  
   
}

if(column.isDynamic) {
   
   // this is a dynamic column
   // code  
   
}

if(column.isUnmapped) {
   
   // this is a unmapped column
   // code  
   
}
```

#### `csvbox.environment`

It contains the [environment variables](/advanced-installation/environment-variables) that are passed during importer initialization.

```javascript
// Example Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

{% hint style="info" %}
&#x20;***console.log(csvbox);***&#x20;

With this statement, you can print all the available variables in the debugging console,
{% endhint %}

### Error Response JSON Format for Row Functions

CSVbox will expect the Row Function to return an array of errors. Each error should specify the `column` the error appeared in, and a `message` to be displayed in the UI.

#### JSON Response Schema

<table><thead><tr><th width="132.33333333333331">Parameter</th><th width="93">Type</th><th>Description</th></tr></thead><tbody><tr><td>column</td><td>string</td><td>The <a href="https://help.csvbox.io/dashboard-settings/sheet-options#column-name">column name </a>of the error. It is case sensitive.</td></tr><tr><td>message</td><td>string</td><td>The message that is to be displayed to the user on the validation screen of the importer.</td></tr></tbody></table>

#### Example Row Function JSON Response

```json
[
  {
    "column": "employee_id",
    "message": "Invalid Emp ID"
  },
  {   
    "column": "dept",
    "message": "Department does not exist"
  },
  {   
    "column": "employee_name",
    "message": "Employee's name has changed"
  }
]
```

## Column Functions

### Adding Column Functions

* Go to the edit sheet page > **Columns** tab > Click **Add Functions** button.
* Add **Function Name**.
* Select **Column** under Function Type.
* Add the Columns you need in the function.
* Provide **Javascript** code.
* Attach **Dependent** libraries (optional).
* Click **Save**.

<figure><img src="/files/6fuxXpVJVG5lBINoZJet" alt=""><figcaption><p>Adding Column Functions</p></figcaption></figure>

{% hint style="info" %}
You can also enter the [Dynamic Column](/advanced-installation/dynamic-columns) names to access them in the Validation Functions.
{% endhint %}

### Example Column Functions

{% tabs %}
{% tab title="Detect Duplicates" %}
Check if a column has duplicate entries.

```javascript
//replace "col4" with actual column name

function findRepeatingIndices(arr) {
  const repeatingIndices = {};
  
  for (let i = 0; i < arr.length; i++) {
    const element = arr[i];
    if (repeatingIndices[element] === undefined) {
      repeatingIndices[element] = [i];
    } else {
      repeatingIndices[element].push(i);
    }
  }
  
  const result = [];
  
  for (const key in repeatingIndices) {
    if (repeatingIndices[key].length > 1) {
      result.push(...repeatingIndices[key]);
    }
  }
  
  return result;
}

const arr = csvbox.column["col4"];
const repeatingIndices = findRepeatingIndices(arr);

let errs = [];

repeatingIndices.forEach(index => {
  errs.push({
    "row_id": (index + 1),
    "column": "col4",
    "message": "Duplicate entry."
  });
});

return errs;
```

{% endtab %}

{% tab title="Multi Column Duplicates" %}

<pre class="language-javascript"><code class="lang-javascript">//Find duplicate entries in multiple columns.

<strong>function findDuplicateIndices(arr1, arr2) {
</strong>  const seen = new Map();
  const duplicates = [];
  arr1.forEach((value1, index) => {
    const value2 = arr2[index];
    const pair = `${value1},${value2}`;
    if (seen.has(pair)) {
      duplicates.push(seen.get(pair), index);
    } else {
      seen.set(pair, index);
    }
  });
  return [...new Set(duplicates)];
}

const duplicates = findDuplicateIndices(csvbox.column.col1, csvbox.column.col2);

let errs = [];

duplicates.forEach(index => {
  errs.push({
    "row_id": (index + 1),
    "column": "col1",
    "message": "Duplicate entry."
  });
  errs.push({
    "row_id": (index + 1),
    "column": "col2",
    "message": "Duplicate entry."
  });
});

return errs;
</code></pre>

{% endtab %}
{% endtabs %}

### Dependencies

In the Column Function Javascript snippet, you can utilize an external library that is hosted via a CDN. You can also call any external API endpoint to fetch real-time data. Simply add the library script tag and/or any custom scripts to the 'Dependencies' section and start using it in the main Javascript snippet.

### Variables in the Column Function

You have access to data variables included in the **`csvbox`** object. The following data is available:

#### `csvbox.column`

It contains the entire column data. Each cell in the column can be accessed by providing the column name and the row number. The row number starts with 1. Examples:

```javascript
csvbox.column["first_name"][1]
csvbox.column["order_id"][12]
csvbox.column["price"][10001]
```

{% hint style="warning" %}
Only the selected columns will be available in the Column Function.
{% endhint %}

#### `csvbox.user`

It contains the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) defined while initializing the importer. Examples:

```javascript
csvbox.user["user_id"]
csvbox.user["team_id"]
csvbox.user["isAuthenticated"]
```

#### `csvbox.import`

This refers to the current import-specific data. The following data is available:

```javascript
csvbox.import["sheet_id"]
csvbox.import["sheet_name"]
csvbox.import["original_filename"]
csvbox.import["import_start_time"]
csvbox.import["destination_type"]
csvbox.import["total_rows"]
csvbox.import["row_number"] //current row number starting with 1
```

#### `csvbox.environment`

It contains the [environment variables](/advanced-installation/environment-variables) that are passed during importer initialization.

```javascript
// Example Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

#### `csvbox.columns`

This object contains the column metadata (name, type).

```javascript
//Example usage
let column = csvbox.columns['birthdate'];

if(column.type == 'date') {

   // code  
   
}

if(column.isDynamic) {
   
   // this is a dynamic column
   // code  
   
}

if(column.isUnmapped) {
   
   // this is a unmapped column
   // code  
   
}
```

{% hint style="info" %}
&#x20;***console.log(csvbox);***&#x20;

With this statement, you can print all the available variables in the debugging console.
{% endhint %}

### Error Response JSON Format for Column Functions

CSVbox will expect the Column Function to return an array of errors. Each error should specify the `row_id`, the `column` the error appeared in, and a `message` to be displayed in the UI.

#### JSON Response Schema for Column Functions

<table><thead><tr><th width="126">Parameter</th><th width="93">Type</th><th>Description</th></tr></thead><tbody><tr><td>row_id</td><td>integer</td><td>The row number of the error. Starts with 1.</td></tr><tr><td>column</td><td>string</td><td>The <a href="https://help.csvbox.io/dashboard-settings/sheet-options#column-name">column name </a>of the error. It is case sensitive.</td></tr><tr><td>message</td><td>string</td><td>The message to be displayed to the user on the validation screen of the importer.</td></tr></tbody></table>

#### Example Column Function JSON Response

```json
[
  {
    "row_id": 1,
    "column": "employee_id",
    "message": "Invalid Emp ID"
  },
  {
    "row_id": 2,
    "column": "dept",
    "message": "Department does not exist"
  },
  {
    "row_id": 3,
    "column": "employee_name",
    "message": "Employee's name has changed"
  }
]
```


# Server Side Validation

Validate data at your server, report back errors for correction.

Consider a case where you want to validate the incoming data against your business rules. This could be as simple as verifying if the user ID is found in the database or something more complex that involves custom logic. Here you want the validation to be done at your server end and relay back errors if any.

With CSVbox you have the option of server-side validation of the submitted data and returning back the errors. Then the users can fix the errors and re-submit the data.

### How it Works

<figure><img src="/files/Uqwp85qHt5Ml4JIyioXu" alt=""><figcaption><p>Server Side Validation</p></figcaption></figure>

#### 1. Activate Server Side Validation via Sheet Settings.

Go to Edit Sheet > Select Destination Tab > Enable Server Side Validation

<div align="left"><figure><img src="/files/QfKMwChbms9deIl2sU7f" alt=""><figcaption><p>Activate Server Side Validation</p></figcaption></figure></div>

{% hint style="warning" %}
The External Validation option is available only for the [API data destination](/destinations#api-webhook).
{% endhint %}

#### 2. The users upload and submit the spreadsheet.

The users upload the spreadsheet, map columns, verify data, and then submit.

#### 3. The CSVbox importer pushes the data to the API endpoint configured by you.

The importer will send the spreadsheet data via POST requests with JSON values to your API endpoint. The request schema is available [here](https://help.csvbox.io/destinations#sample-json-post-to-your-api).

#### 4. Your app can then processes the data and validate it against the business rules.

Case 1: Validation is successful - no errors found. Your API returns a **`200`** HTTP response code. The success screen is displayed to the user.

<figure><img src="/files/SH4SDmhKI7zTbpR7wsQy" alt=""><figcaption><p>Success Screen</p></figcaption></figure>

Case 2: Validation failed - one or more errors found. Your API returns **`211`** HTTP response code along with the validation errors in JSON format. The error response JSON format is mentioned [here](#error-types).

{% hint style="info" %}
It is mandatory for your API to return **`211`** HTTP response status code to instruct the CSVbox importer that there are one or more server-side validation errors.
{% endhint %}

{% hint style="warning" %}
To view the results screen be sure to configure the CSVbox Result Page Settings. Go to Sheet Settings > Display > Results Page > Set **Closing the import dialog box** to **Do not close on import complete**

![](/files/TOiNDX4KeVoZkTAvcxmV)
{% endhint %}

#### 5. Validation Fail Screen is displayed to the user.

If there are one or more server-side validation errors then the users will see the Fail Screen with a button to view the errors.

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

#### 6. Users can view the validation errors.

Clicking on the Errors button will take the users to the Verify Data screen with all the server-side errors displayed.

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

#### 7. After fixing the errors, the users can re-submit the data.

On re-submitting the data, the process will repeat. The importer will push the data to your API endpoint via POST requests and look for errors in the response.

{% hint style="info" %}
Each re-submit will be treated as a fresh import having a new **`Import_Id`**.
{% endhint %}

To allow the users to re-submit all the rows again (instead of error rows only) select the 'All Rows' option as shown below:

<div align="left"><figure><img src="/files/RXnmSsyFw3WrI2f32SDh" alt="" width="326"><figcaption><p>Re-submit All Rows</p></figcaption></figure></div>

### Error Types

CSVBox server-side validation now supports three error types:

* **table**: Show high-level/common errors that apply to the entire upload. Renders as a dismissible alert above the grid.
* **row**: Show errors that apply to a whole row, not a specific cell. Renders as a red badge on the row number; clicking it opens a pop-up with the message.
* **column**: Show validation errors that apply to an entire column. These are useful when the issue is with the column itself rather than with any individual row or cell.
* **cell**: The previous behavior; highlights an individual cell with an inline message. In this documentation we now refer to these as “cell errors.”

**UI behavior**

* **Table errors** appear in a prominent alert banner above the grid until dismissed or replaced by a subsequent validation run.
* **Row errors** highlight the row index with a red badge. Clicking the badge opens a pop-over containing your `message`.
* **Column errors** highlight the column name with a red badge. Clicking the badge opens a pop-over containing your `message`.
* **Cell errors** continue to highlight individual cells with inline messages as before.

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

**When to use each type**

* **table**: Missing required columns, inconsistent file format, duplicate file, or any condition that makes the whole dataset invalid.
* **row**: Cross-field checks within the same row (e.g., “End Date must be after Start Date”), referential issues that aren’t tied to a single column, or row-level business rules.
* **column**: Use column errors when the issue applies to a full column rather than to one cell or row (e.g. Invalid column name, a required column has an incorrect format or configuration).&#x20;
* **cell**: Format/length/pattern errors, disallowed values, or any validation that is clearly attributable to one column.

### Response Format JSON

CSVbox will expect the validation endpoint to return an array of error objects.

**Fields**

<table><thead><tr><th width="132.33333333333331">Parameter</th><th width="93">Type</th><th>Description</th></tr></thead><tbody><tr><td>type</td><td>string</td><td><code>"table" | "row" | "column" | "cell"</code>. Defaults to <code>"cell"</code>. (Optional)</td></tr><tr><td>row_id</td><td>integer</td><td>The row number of the error. Starts with 1. (required for <code>row</code> and <code>cell</code>)</td></tr><tr><td>column</td><td>string</td><td>The CSVbox <a href="https://help.csvbox.io/dashboard-settings/sheet-options#column-name">column name </a> (i.e., the field you mapped), not the user’s original header.. It is case sensitive. (required for <code>column</code> and <code>cell</code>)</td></tr><tr><td>message</td><td>string</td><td>String to display to the user. Basic HTML line breaks like <code>&#x3C;br></code> are supported.</td></tr></tbody></table>

#### Example payload

```json
[
  {
    "type": "table",
    "message": "Missing address.<br>Missing department.<br>Resubmit."
  },
  {
    "type": "cell",
    "row_id": 1,
    "column": "employee_id",
    "message": "Invalid Emp ID"
  },
  {
    "row_id": 2,
    "column": "dept",
    "message": "Department does not exist"
  },
  {
    "row_id": 3,
    "column": "employee_name",
    "message": "Employee's name has changed"
  },
  {
    "type": "row",
    "row_id": 3,
    "message": "Cannot add this row"
  },
  {
     "type": "column",
     "column": "employee_id",
     "message": "Invalid column name"
  }
]
```

You can mix **table**, **row**, **column** and **cell** errors in the same response.

### Additional Attributes in the Client Data Object

When **Server-Side Validation (SSV)** is enabled, the data object received at the client (after submission) includes additional attributes to help you handle validation states programmatically.

<table><thead><tr><th width="197.20001220703125">Attribute</th><th>Description</th></tr></thead><tbody><tr><td><strong>ssv_enabled</strong></td><td>Indicates whether Server-Side Validation is active for the current sheet.</td></tr><tr><td><strong>ssv_fail</strong></td><td>Set to <strong>true</strong> only when the server returns a <strong>211 response code</strong>, meaning the import failed due to SSV errors.</td></tr><tr><td><strong>ssv_row_fail</strong></td><td>Number of rows that failed during server-side validation.</td></tr><tr><td><strong>ssv_table_error</strong></td><td>True if a table-level validation error was returned by the server.</td></tr></tbody></table>

These attributes allow you to detect and respond to validation outcomes—such as displaying custom messages, logging failed rows, or triggering retry logic—directly from your client application.

**Example Response Object**

```json
{
  destination_type: "apiwebhook",
  env_name: "default", 
  import_description: "",
  import_endtime: 1762774689,
  import_id: 12859532,
  import_starttime: 1762774686,
  import_status: "success",
  original_filename: "emp.csv",
  raw_file: "https://app.csvbox.io/download-rawfile/1-FIIeyMflK9OvAX1EMjncCTlMasuG5Vil35LQNHAQ",
  row_count: 5,
  row_fail: 0,
  row_success: 5,
  sheet_id: 1058,
  sheet_name: "Customer onboarding",
  ssv_enabled: 1,
  ssv_fail: 1,
  ssv_row_fail: 3,
  ssv_table_error: 1
}

```

In this example:

* **ssv\_enabled** confirms that SSV is active.
* **ssv\_fail** is `true` because the server responded with a **211** code.
* **ssv\_row\_fail** specifies the number of failed rows.
* **ssv\_table\_error** is `false`, meaning the issue was at the row level rather than a table-level error.


# Data Transforms

Bulk edit the dataset before pushing it to your system.

The **Data Transforms** feature in CSVbox empowers you to modify and manipulate the data before it is uploaded to your app. Using JavaScript, you can apply custom transformations to reshape, sanitize, or enhance your data in real-time. Whether you need to perform simple tasks like capitalizing text or formatting dates, or handle complex business logic, Data Transforms gives you full control to customize your dataset to meet your app's specific needs.

<figure><img src="/files/gawT94fZ6rjwMJC4u92X" alt=""><figcaption><p>Data Trasforms</p></figcaption></figure>

## How it works

When a CSV file is uploaded, CSVbox parses the data and applies your transformation logic row-by-row or column cell-by-cell. The JavaScript function you write defines how each row or field should be transformed. Once the transformation is complete, the modified data is passed to the next stage for validation.\
\
There are two main types of Data Transforms available in CSVbox: **Row Transforms** and **Column Transforms**.

* **Row Transforms** apply transformation logic row-by-row, processing each row individually from top to bottom. These are especially useful when the transformation of one cell depends on the values of other cells within the same row. For instance, if you need to combine data from multiple cells into a single cell, or if a calculation requires input from different columns within the same row, Row Transforms are ideal. This approach allows you to apply logic dynamically based on relationships within each row of data.
* **Column Transforms** operate on a single column or a set of selected columns, applying transformations to all entries in the specified columns at once. These transforms are most effective when you need to analyze or modify data across the entire column rather than row-by-row. For example, if you're looking to identify duplicate entries in a column and replace each duplicate with a unique identifier, a Column Transform can help by analyzing all values in that column collectively before applying changes. This approach is efficient for transformations that rely on examining the column as a whole, such as sorting, aggregating, or deduplication tasks.

Using these two transformation types, you can precisely target and manipulate your data based on your application's requirements, whether you need to perform intra-row calculations or make adjustments to column-wide data sets.

{% hint style="warning" %}
Selecting **Column Transforms** can impact importer performance, as the entire column dataset is loaded into memory for processing. This means that, especially with large datasets, using Column Transforms may slow down the import process. For optimal performance, consider using Row Transforms for operations that do not require analyzing the entire column, reserving Column Transforms for cases where column-wide data processing is essential, such as deduplication or aggregation tasks.
{% endhint %}

## Row Transforms <a href="#adding-virtual-columns" id="adding-virtual-columns"></a>

### Adding Row Data Transforms <a href="#adding-virtual-columns" id="adding-virtual-columns"></a>

1. Go to the edit sheet page > **Data Transforms** tab >  Click **Add Transforms** button.
2. Add Transform **Name**.
3. Select **Transform Type** as **Row**.
4. Provide **Javascript** code.
5. Attach **Dependent** Libraries (optional).
6. Click **Save**.

### Row Transform Examples

{% tabs %}
{% tab title="Append text " %}
Append a constant to the cell value.

{% code overflow="wrap" %}

```javascript
csvbox.row["serial_number"] = csvbox.row["serial_number"] + '_' + csvbox.user["user_id"];
  
  return csvbox;
```

{% endcode %}
{% endtab %}

{% tab title="Normalize dates" %}
Normalize date value into US format.

{% code overflow="wrap" %}

```javascript
function normalizeToUSFormat(dateString) {
  // Create a new Date object by parsing the input date string
  const date = new Date(dateString);

  // Check if the Date object is valid
  if (isNaN(date.getTime())) {  
    console.log("Invalid date format.");
    return dateString;
  }

  // Get month, day, and year
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const year = date.getFullYear();

  // Return the date in MM/DD/YYYY format
  return `${month}/${day}/${year}`;
}

// Example usage
  csvbox.row["date_of_birth"] = normalizeToUSFormat(csvbox.row["date_of_birth"]);
 
  return csvbox;
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Variables in the Row Transform

You have access to data variables included in the **`csvbox`** object. The following data is available:

#### `csvbox.row`

It contains row data. Each cell in the row can be accessed by providing the column name. Examples:

```javascript
csvbox.row["first_name"]
csvbox.row["order_id"]
csvbox.row["price"]
```

#### `csvbox.user`

It contains the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) defined while initializing the importer. Examples:

```javascript
csvbox.user["user_id"]
csvbox.user["team_id"]
csvbox.user["isAuthenticated"]
```

#### `csvbox.import`

This refers to the current import-specific data. The following data is available:

```javascript
csvbox.import["sheet_id"]
csvbox.import["sheet_name"]
csvbox.import["original_filename"]
csvbox.import["import_start_time"]
csvbox.import["destination_type"]
csvbox.import["total_rows"]
csvbox.import["row_number"] //current row number starting with 1
```

#### `csvbox.columns`

This object contains the column metadata (name, type).

```javascript
//Example usage
let column = csvbox.columns['birthdate'];

if(column.type == 'date') {

   // code  
   
}

if(column.isDynamic) {
   
   // this is a dynamic column
   // code  
   
}

if(column.isUnmapped) {
   
   // this is a unmapped column
   // code  
   
}

```

#### `csvbox.environment`

It contains the [environment variables](/advanced-installation/environment-variables) that are passed during importer initialization.

```javascript
// Example Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

{% hint style="info" %}
&#x20;***console.log(csvbox);***&#x20;

With this statement, you can print all the available variables in the debugging console.
{% endhint %}

## Column Transforms <a href="#adding-virtual-columns" id="adding-virtual-columns"></a>

### Adding Column Data Transforms <a href="#adding-virtual-columns" id="adding-virtual-columns"></a>

1. Go to the edit sheet page > **Data Transforms** tab >  Click **Add Transforms** button.
2. Add Transform **Name**.
3. Add the **Columns you need for** the transform.
4. Provide **Javascript** code.
5. Attach **Dependent** Libraries (optional).
6. Click **Save**.

### Column Transform Examples

{% tabs %}
{% tab title="Example 1" %}
Capitalizing Text Fields:

```javascript
//loop and capitalize each value

for(let i=0; i < csvbox.column["first_name"].length; i++)
{
    csvbox.column["first_name"][i] = csvbox.column["first_name"][i].toUpperCase();
}

// return the updated data set
return csvbox;
```

{% endtab %}
{% endtabs %}

### Variables in the Column Transform

You have access to data variables included in the **`csvbox`** object. The following data is available:

#### `csvbox.column`

It contains the entire column data. Each cell in the column can be accessed by providing the column name and the row number. The row number starts with 1. Examples:

```javascript
csvbox.column["first_name"][1]
csvbox.column["order_id"][12]
csvbox.column["price"][10001]
```

{% hint style="warning" %}
Only the selected columns will be available in the Transform javascript.
{% endhint %}

#### `csvbox.user`

It contains the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) defined while initializing the importer. Examples:

```javascript
csvbox.user["user_id"]
csvbox.user["team_id"]
csvbox.user["isAuthenticated"]
```

#### `csvbox.import`

This refers to the current import-specific data. The following data is available:

```javascript
csvbox.import["sheet_id"]
csvbox.import["sheet_name"]
csvbox.import["original_filename"]
csvbox.import["import_start_time"]
csvbox.import["destination_type"]
csvbox.import["total_rows"]
```

#### `csvbox.environment`

It contains the [environment variables](/advanced-installation/environment-variables) that are passed during importer initialization.

```javascript
// Example Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

#### `csvbox.columns`

This object contains the column metadata (name, type).

```javascript
//Example usage
let column = csvbox.columns['birthdate'];

if(column.type == 'date') {

   // code  
   
}

if(column.isDynamic) {
   
   // this is a dynamic column
   // code  
   
}

if(column.isUnmapped) {
   
   // this is a unmapped column
   // code  
   
}
```

#### ***console.log(csvbox);***&#x20;

{% hint style="info" %}
With this statement, you can print all the available variables in the debugging console.
{% endhint %}

### Dependencies

In the Transforms Javascript snippet, you can utilize an external library hosted via a CDN. You can also call any external API endpoint to fetch real-time data. Add the library script tag and/or any custom scripts to the 'Dependencies' section and use it in the main Javascript snippet.

## **Execution Time**

Every Data Transform can now run at **one of two stages** in the import pipeline:

* **Pre Data Validation** (default)
* **Post Data Validation**

This gives you full control over *when* your transform runs during the import workflow.

#### **1. Pre-Validation Data Transform**

Runs **immediately after column mapping** and **before Data Validation**.

Use this for:

* Cleaning or normalizing user input
* Converting formats (dates, numbers, booleans)
* Combining or splitting fields
* Setting defaults
* Preparing data that validators depend on

Example:\
Convert a string such as `" 23.4500 "` into a clean numeric value before your validation rules run.

#### **2. Post-Validation Data Transform**

Runs **only if there are no validation errors**, and just before pushing data to the final destination.

Use this for:

* Final formatting
* Destination-specific transformations
* Generating or enriching fields **only when the row is already valid**
* Preparing computed values needed only for storage

Example:\
If you need to generate a `slug`, `UUID`, or apply pricing markup *after* all validations pass.

#### **Where to Configure Execution Time**

When creating or editing a Data Transform:

1. Open **Sheet Settings → Data Transforms**.
2. Select your existing transform or create a new one.
3. Choose the desired **Execution Time**:
   * **Pre Data Validation**
   * **Post Data Validation**
4. Save your changes.

## Key Features

* **JavaScript-Powered**: Write custom JavaScript code to perform operations on your data. With access to native JavaScript methods, you can implement transformations ranging from basic modifications to complex logic.
* **Real-Time Execution**: Your transformations are applied as the file is uploaded, ensuring that the data is processed and adjusted before it enters your app.
* **Versatile**: Use Data Transforms to handle a wide range of operations, such as:
  * Modifying string data (e.g., capitalizing text, trimming spaces)
  * Formatting dates
  * Performing calculations, such as currency conversions
  * Validating and cleaning up data
  * Applying conditional logic to data fields
* **Error Handling**: You can include custom error handling in your JavaScript to manage issues gracefully without disrupting the upload process.

## Best Practices

* **Error Handling**: Incorporate error-handling logic into your JavaScript code to avoid disruptions in the upload process due to malformed data.
* **Optimize Performance**: Keep your transformation code efficient, especially when working with large datasets, to ensure smooth uploads.

## Conclusion

With Data Transforms, you have the flexibility to clean, format, and manipulate data in real-time as it's uploaded through CSVbox. Whether you need to perform simple formatting tasks or implement complex business logic, Data Transforms can help you streamline your data handling processes and ensure that your datasets are always consistent and accurate.


# REST File API

Automate CSV submissions via REST File API

The File API lets you accept spreadsheet files programmatically. It is an alternative to the users uploading the files manually, via the Csvbox importer. Files submitted via the REST File API will then be pushed to the data destination as set up in the Csvbox dashboard.

#### Simple File API Request

```javascript
curl --location --request POST 'https://api.csvbox.io/1.1/file' \
--header 'x-csvbox-api-key: CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv' \
--header 'Content-Type: application/json' \
--data-raw '{
    "import": {
        "public_file_url": "https: //some-domain.com/file/3434as.csv",
        "sheet_license_key": "jhkjsahjkhkjhkjhkjasdasd",
        "user": {
            "user_id": "1a2b3c4d5e6f",           
        },
         "options": {           
            "has_header": 1
        }    
}'
```

After the data is pushed to the destination, the [import complete webhook ](/getting-started/3.-receive-data#import-complete-webhook)can be triggered as configured in the sheet settings.

{% hint style="warning" %}
The spreadsheet data submitted via File API will not be [validated](https://help.csvbox.io/validations) based on the rules configured in the sheet settings. The importer will attempt to push the data directly to the destination in the raw form.
{% endhint %}

Authentication

All REST File API queries require a valid API key. You can find the API key on the **Accounts** page in the Csvbox dashboard.

Include your API key as a `x-csvbox-api-key` header on all API queries.&#x20;

### Endpoint

## Submits a spreadsheet file

<mark style="color:green;">`POST`</mark> `https://api.csvbox.io/1.1/file`

#### Headers

| Name                                               | Type   | Description      |
| -------------------------------------------------- | ------ | ---------------- |
| x-csvbox-api-key<mark style="color:red;">\*</mark> | String | API Key          |
| content-type<mark style="color:red;">\*</mark>     | String | application/json |

#### Request Body

| Name                                                         | Type   | Description                                                                                  |
| ------------------------------------------------------------ | ------ | -------------------------------------------------------------------------------------------- |
| import.public\_file\_url<mark style="color:red;">\*</mark>   | String | The public URL of the spreadsheet that is to be submitted.                                   |
| import.file\_sheet\_name                                     | String | Worksheet name in case of a file having multiple tabs.                                       |
| import.sheet\_license\_key<mark style="color:red;">\*</mark> | String | Sheet license key                                                                            |
| import<mark style="color:red;">\*</mark>                     | Object | Import file data                                                                             |
| import.user                                                  | Object | Object [referencing the user](https://help.csvbox.io/getting-started#referencing-the-user)   |
| import.options                                               | Object | Object [referencing the import options](#additional-options)                                 |
| import.dynamic\_columns                                      | Object | Object [referencing dynamic columns](https://help.csvbox.io/getting-started/dynamic-columns) |

{% hint style="warning" %}
The columns in the file should be arranged in the same order as configured in the sheet template and the column names should match exactly. If you want the importer to auto-map the columns, configure **options**.**auto\_map** to **true**.
{% endhint %}

{% tabs %}
{% tab title="200: OK File submitted" %}

```javascript
HTTP/1.1 200 OK
{
  "import_id": 79418895,
  "license_key":"jhkjsahjkhkjhkjhkjasdasd",
  "sheet_id": 575,
  "sheet_name": "Products Import",
  "destination_type": "webhook",
  "import_starttime": 87987897897,
  "custom_fields": {
    "user_id": "1a2b3c4d5e6f",
    "team_id": "sales2",
    "permissionLevel": "admin"
  },
  "options": {
    "max_rows": 150,
    "has_header": 1,
    "auto_map": true
  },
  "dynamic_olumns": [
     {
      "column_name": "qualification",
      "display_label": "Highest Qualification",
      "info_hint": "What is your highest educational degree",
      "matching_keywords": "degree, education",
      "type": "text",
      "validators": {
        "min_length": 2,
        "max_length": 50
      },
      "required": true
    },
    {
      "column_name": "experience",
      "display_label": "Work Experience",
      "info_hint": "Years of work experience",
      "matching_keywords": "",
      "type": "number",
      "validators": {
        "min_value": 0,
        "max_value": 100
      },
      "required": false
    }
  ]
}
```

{% endtab %}
{% endtabs %}

#### Example Request

{% tabs %}
{% tab title="cURL" %}

```javascript
curl --location --request POST 'https://api.csvbox.io/1.1/file' \
--header 'x-csvbox-api-key: CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv' \
--header 'Content-Type: application/json' \
--data-raw '{
    "import": {
        "public_file_url": "https: //some-domain.com/admin/download-csv/UXOR2MphpEu2sSAIISpY7AKmOIzAKygLNy8eviEr",
        "sheet_license_key": "jhkjsahjkhkjhkjhkjasdasd",
        "user": {
            "user_id": "1a2b3c4d5e6f",
            "team_id": "sales2",
            "permissionLevel": "admin"
        },
        "options": {
            "max_rows": 150,
            "has_header": 1
        },
        "dynamic_columns": [
            {
                "column_name": "qualification",
                "display_label": "HighestQualification",
                "info_hint": "Whatisyourhighesteducationaldegree",
                "matching_keywords": "degree,   education",
                "type": "text",
                "validators": {
                    "min_length": 2,
                    "max_length": 50
                },
                "required": true
            },
            {
                "column_name": "experience",
                "display_label": "WorkExperience",
                "info_hint": "Yearsofworkexperience",
                "matching_keywords": "",
                "type": "number",
                "validators": {
                    "min_value": 0,
                    "max_value": 100,
                    "auto_map": true
                },
                "required": false
            }
        ]
    }'
```

{% endtab %}

{% tab title="jQuery" %}

```javascript
var settings = {
  "url": "https://api.csvbox.io/1.1/file",
  "method": "POST",
  "timeout": 0,
  "headers": {
    "x-csvbox-api-key": "CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv",
    "Content-Type": "application/json"
  },
  "data": "{\r\n    \"import\": {\r\n        \"public_file_url\": \"https: //some-domain.com/admin/download-csv/UXOR2MphpEu2sSAIISpY7AKmOIzAKygLNy8eviEr\",\r\n        \"sheet_license_key\": \"jhkjsahjkhkjhkjhkjasdasd\",\r\n        \"user\": {\r\n            \"user_id\": \"1a2b3c4d5e6f\",\r\n            \"team_id\": \"sales2\",\r\n            \"permissionLevel\": \"admin\"\r\n        },\r\n        \"options\": {\r\n            \"max_rows\": 150,\r\n            \"has_header\": \"1\"\r\n        },\r\n        \"dynamic_columns\": [\r\n            {\r\n                \"column_name\": \"qualification\",\r\n                \"display_label\": \"HighestQualification\",\r\n                \"info_hint\": \"Whatisyourhighesteducationaldegree\",\r\n                \"matching_keywords\": \"degree,   education\",\r\n                \"type\": \"text\",\r\n                \"validators\": {\r\n                    \"min_length\": 2,\r\n                    \"max_length\": 50\r\n                },\r\n                \"required\": true\r\n            },\r\n            {\r\n                \"column_name\": \"experience\",\r\n                \"display_label\": \"WorkExperience\",\r\n                \"info_hint\": \"Yearsofworkexperience\",\r\n                \"matching_keywords\": \"\",\r\n                \"type\": \"number\",\r\n                \"validators\": {\r\n                    \"min_value\": 0,\r\n                    \"max_value\": 100\r\n                },\r\n                \"required\": false\r\n            }\r\n        ]\r\n    }",
};

$.ajax(settings).done(function (response) {
  console.log(response);
});
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://api.csvbox.io/1.1/file',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
    "import": {
        "public_file_url": "https: //some-domain.com/admin/download-csv/UXOR2MphpEu2sSAIISpY7AKmOIzAKygLNy8eviEr",
        "sheet_license_key": "jhkjsahjkhkjhkjhkjasdasd",
        "user": {
            "user_id": "1a2b3c4d5e6f",
            "team_id": "sales2",
            "permissionLevel": "admin"
        },
        "options": {
            "max_rows": 150,
            "has_header": 1
        },
        "dynamic_columns": [
            {
                "column_name": "qualification",
                "display_label": "HighestQualification",
                "info_hint": "Whatisyourhighesteducationaldegree",
                "matching_keywords": "degree,   education",
                "type": "text",
                "validators": {
                    "min_length": 2,
                    "max_length": 50
                },
                "required": true
            },
            {
                "column_name": "experience",
                "display_label": "WorkExperience",
                "info_hint": "Yearsofworkexperience",
                "matching_keywords": "",
                "type": "number",
                "validators": {
                    "min_value": 0,
                    "max_value": 100
                },
                "required": false
            }
        ]
    }',
  CURLOPT_HTTPHEADER => array(
    'x-csvbox-api-key: CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv',
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
var axios = require('axios');
var data = '{\r\n    "import": {\r\n        "public_file_url": "https: //some-domain.com/admin/download-csv/UXOR2MphpEu2sSAIISpY7AKmOIzAKygLNy8eviEr",\r\n        "sheet_license_key": "jhkjsahjkhkjhkjhkjasdasd",\r\n        "user": {\r\n            "user_id": "1a2b3c4d5e6f",\r\n            "team_id": "sales2",\r\n            "permissionLevel": "admin"\r\n        },\r\n        "options": {\r\n            "max_rows": 150,\r\n            "has_header": "1"\r\n        },\r\n        "dynamic_columns": [\r\n            {\r\n                "column_name": "qualification",\r\n                "display_label": "HighestQualification",\r\n                "info_hint": "Whatisyourhighesteducationaldegree",\r\n                "matching_keywords": "degree,   education",\r\n                "type": "text",\r\n                "validators": {\r\n                    "min_length": 2,\r\n                    "max_length": 50\r\n                },\r\n                "required": true\r\n            },\r\n            {\r\n                "column_name": "experience",\r\n                "display_label": "WorkExperience",\r\n                "info_hint": "Yearsofworkexperience",\r\n                "matching_keywords": "",\r\n                "type": "number",\r\n                "validators": {\r\n                    "min_value": 0,\r\n                    "max_value": 100\r\n                },\r\n                "required": false\r\n            }\r\n        ]\r\n    }';

var config = {
  method: 'post',
  url: 'https://api.csvbox.io/1.1/file',
  headers: { 
    'x-csvbox-api-key': 'CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv', 
    'Content-Type': 'application/json'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

```

{% endtab %}
{% endtabs %}

### Direct File Upload

As an alternative to providing the public file URL to the FILE API, you also have the option to upload the file contents directly. Instead of using the *import.public\_file\_url* use the following body param:

#### Request Body

| Name                                          | Type   | Description                              |
| --------------------------------------------- | ------ | ---------------------------------------- |
| import.file<mark style="color:red;">\*</mark> | String | The contents of the file to be imported. |

{% hint style="info" %}
The file should be sent with the `multipart/form-data` content-type.
{% endhint %}

#### Example Request

{% tabs %}
{% tab title="cURL" %}

```javascript
curl --location --request POST 'https://api.csvbox.io/1.1/file' \
--header 'x-csvbox-api-key: CSBxRLHgIZv3bqMlrJiVKXhKwtcHSv' \
--header 'Content-Type: multipart/form-data' \
-F 'import={
        "sheet_license_key": "jhkjsahjkhkjhkjhkjasdasd",
        "user": {
            "user_id": "1a2b3c4d5e6f",           
        },
         "options": {           
             "max_rows": 150,
             "has_header": 1
        }
    }' \
-F 'file=@/path/to/your/file.csv'
```

{% endtab %}
{% endtabs %}

### Rate limits

The REST Admin API supports a limit of 30 requests per minute. This allotment replenishes at a rate of 2 requests per second.&#x20;

Past the limit, the API will return a `429 Too Many Requests` error.

All REST API responses include the x`-csvbox-file-api-call-limit` header, which shows how many requests the client has made, and the total number allowed per minute.

A `429` response will also include a `retry-after` header with the number of seconds to wait until retrying your query.

### Status and error codes

All API queries return HTTP status codes that can tell you more about the response.

**401 Unauthorized**

The client doesn’t have the correct authentication credentials.

```
HTTP/1.1 401 Unauthorized
{
    "errors": "[API] Invalid API key or access token."
}
```

**402 Import Limit Reached**

The monthly import limit based on the account plan is reached. You can upgrade the plan to increase the import limit.

```
HTTP/1.1 402 Import Limit Reached
{
    "errors": "The account has exceeded the import limit."
}
```

**403 Forbidden**

The server is refusing to respond. This is typically caused if File API is disabled.

```
HTTP/1.1 403 Access Denied
{
    "errors": "User does not have access."
}
```

**404 Not Found**

The referenced sheet was not found.

```
HTTP/1.1 404 Not Found
{
    "errors": "Sheet not found."
}
```

**422 Unprocessable File**

The request body contains file errors. This is typically caused by incorrect file path, invalid file permissions, incorrect file format, corrupt file, file size too large, etc.

```
HTTP/1.1 422 Unprocessable File
{
    "errors": "Invalid file format."
}
```

**429 Too Many Requests**

The client has exceeded the [rate limit](#rate-limits).

```
HTTP/1.1 429 Too Many Requests
{
    "errors": "API Rate limit reached. Reduce request rates to resume uninterrupted service."
}
```

**5xx Errors**

An internal error occurred in Csvbox. Report to our team for such errors.

```
HTTP/1.1 500 Internal Server Error
{
    "errors": "An unexpected error occurred."
}
```

### Options

Here is the list of additional configuration options available with the File API.

#### max\_rows

<table><thead><tr><th width="150"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>Type</td><td>Integer</td><td></td></tr><tr><td>Default</td><td>Null</td><td></td></tr><tr><td>Description</td><td>The maximum number of rows to import.</td><td></td></tr></tbody></table>

#### has\_header

<table><thead><tr><th width="150"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>Type</td><td>Boolean</td><td></td></tr><tr><td>Default</td><td>0</td><td></td></tr><tr><td>Description</td><td><p>Specify whether the file contains a header row.<br><br>Acceptable values are:</p><p><strong>0</strong> (no header)</p><p><strong>1</strong> (has a header row)</p></td><td></td></tr></tbody></table>

#### auto\_map

<table><thead><tr><th width="150"></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>Type</td><td>Boolean</td><td></td></tr><tr><td>Default</td><td>Null</td><td></td></tr><tr><td>Description</td><td>Enable automatic column mapping in cases where exact match is not found. <strong>auto_map</strong> default value is <strong>false</strong>.</td><td></td></tr></tbody></table>


# Environment Variables

Variables to configure the importer environment

Environment variables are dynamic values defined during importer initialization and then can be accessed in the sheet configuration. They enable you to configure the environment of your importer.

The variable values passed during importer initialization will replace the placeholders referenced in the sheet settings using double curly braces.

### Defining Environment Variables

1. Create an object **environment**.
2. Add the variables to this object in the \<key>: \<value> format.
3. Pass the **environment** object as a parameter to the importer initialization function.

{% tabs %}
{% tab title="Javascript" %}
Pass the **environment** object to the **CSVBoxImporter** function as shown below:

```javascript
<button class="btn btn-primary" data-csvbox disabled onclick="importer.openModal();">Import</button>
<script type="text/javascript" src="https://js.csvbox.io/script.js"></script>
<script type="text/javascript">
    function callback(result, data) {
        if(result){
            console.log("success");
            console.log(data.row_success + " rows uploaded");
            //custom code
        }else{
            console.log("fail");
            //custom code
        }
    }
    
    let importer = new CSVBoxImporter("YOUR_LICENSE_KEY_HERE", {}, callback, 
    {
	lazy: true,
	environment: {
           env_name: 'staging',
           base_url: "https://staging.mydomain.com",
           authorized_domain: "https://staging.myapp.com",
           user_id: "default123"                      
        }
    });
    
    importer.setUser({
        user_id: "default123"
    })
</script>
```

{% endtab %}

{% tab title="React" %}
{% hint style="warning" %}
Minimum version 1.1.11 of the @csvbox/react library is required to use this feature.
{% endhint %}

Pass environment variables as an object to the **`environment`**&#x70;roperty of the **`CSVBoxButton`** component.

```jsx
<CSVBoxButton
  licenseKey="YOUR_LICENSE_KEY_HERE"
  user={{
    user_id: "default123"
  }}  
   environment={{
     env_name: 'staging',     
     base_url: "https://staging.mydomain.com",
     authorized_domain: "https://staging.myapp.com",
     user_id: "default123"   
  }}
  
  onImport={(result, data) => {
    if(result){
      console.log("success");
      console.log(data.row_success + " rows uploaded");
      //custom code
    }else{
      console.log("fail");
      //custom code
    }
  }} 
  render={(launch, isLoading)=>{
          return <button disabled={isLoading} onClick={launch}>Upload file</button>;
      }}
>
  Import
</CSVBoxButton>
```

{% endtab %}

{% tab title="Angular" %}
{% hint style="warning" %}
Minimum version 1.1.12 of the @csvbox/angular library is required to use this feature.
{% endhint %}

Pass environment variables as an object to the **`environment`**&#x70;roperty of the AppComponent.&#x20;

```javascript
@Component({
  selector: 'app-root',
  template: `
    <csvbox-button
      [licenseKey]="licenseKey"
      [user]="user"      
      [environment]="environment"     
      [imported]="imported.bind(this)">
      Import
    </csvbox-button>
  `
})

export class AppComponent {

  title = 'example';
  licenseKey = 'YOUR_LICENSE_KEY_HERE';
  user = { user_id: 'default123' };
  environment = {
    env_name: 'staging',
    base_url: "https://staging.mydomain.com",
    authorized_domain: "https://staging.myapp.com",
    user_id: "default123"     
  };

  imported(result: boolean, data: any) {
    if(result) {
      console.log("Sheet uploaded successfully");
      console.log(data.row_success + " rows uploaded");
    }else{
      console.log("There was some problem uploading the sheet");
    }
  }
}
```

{% endtab %}

{% tab title="Vuejs" %}
{% hint style="warning" %}
Minimum version 1.1.8 of the @csvbox/vuejs library is required to use this feature.

Minimum version 1.1.5 of the @csvbox/vuejs3 library is required to use this feature.
{% endhint %}

Pass environment variables as an object to the **`environment`**&#x70;roperty of the **`CSVBoxButton`** component.&#x20;

```javascript
<template>
  <div id="app">
    <CSVBoxButton 
      :licenseKey="licenseKey"
      :user="user"        
      :environment="environment"        
      :onImport="onImport">
      Upload File
    </CSVBoxButton>
  </div>
</template>

<script>
import { CSVBoxButton } from '@csvbox/vuejs';

export default {
  name: 'App',
  components: {
    CSVBoxButton,
  },
  data: () => ({
    licenseKey: 'YOUR_LICENSE_KEY_HERE',
    user: {
      user_id: 'default123',
    },
     environment = {
      env_name: 'staging',
      base_url: "https://staging.mydomain.com",
      authorized_domain: "https://staging.myapp.com",
      user_id: "default123"    
    },
  }),
  methods: {    
    onImport: function (result, data) {    
       if(result){
          console.log("success");
          console.log(data.row_success + " rows uploaded");
          //custom code
      }else{
          console.log("fail");
          //custom code
      }
    }
  },
}
</script>
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Environment variable names should not contain spaces and other special characters apart from \_ (underscore).
{% endhint %}

The **env\_name** variable name is a CSVbox reserved keyword that tags the environment. The import will be categorized in environment based on its value. You can pass values such as 'production', 'staging', 'local' to categorize the import. If no value is passed then the **env\_name** variable will default to 'default' value. The individual imports can be filtered using the **env\_name** inside CSVbox admin dashboard.

<div align="left"><figure><img src="/files/5zspyGlO7p1w4I6opseo" alt=""><figcaption><p>Filter via Environment on the Import page</p></figcaption></figure></div>

### Accessing the Variables

Environment variable values will replace all the environment placeholders mentioned in the sheet settings. The placeholders are defined using double curly brackets:

*{{ environment\_variable\_name }}*.

For instance, to reference an environment variable named "base\_url," use the following syntax with double curly braces around the variable name:

`{{base_url}}/route-endpoint`

Using environment variables in the webhook URL allows you to dynamically adjust the webhook route based on the specific environment.

<div align="left"><figure><img src="/files/dSFYtMNE5LHCDTrgUjmE" alt="" width="563"><figcaption><p>Defining Environment Placeholder</p></figcaption></figure></div>

Apart from defining the environment, these variables can be used to dynamically configure the importer based on end users or any other system parameters. For example, you can define an environment variable **user\_id** and pass it to the webhook URL to dynamically configure the URL based on the end user.

<div align="left"><figure><img src="/files/mZERR8EnsltCbIyg2CG9" alt="" width="360"><figcaption><p>user_id env variable</p></figcaption></figure></div>

Environment variables can also be accessed in [Validation Functions](/advanced-installation/validation-functions), [Virtual Columns](/advanced-installation/virtual-columns) and [Data Transforms](/advanced-installation/data-transforms) Javascript code.

```javascript
// Usage
if(csvbox.environment["user_id"] && csvbox.environment["user_id"] == "abc123") {
 
 // code
 
}
```

### Encrypting Environment Variables

You can encrypt environment variables using the [AES Everywhere library ](https://github.com/mervick/aes-everywhere)to protect sensitive data.

#### AES Everywhere Library

The AES Everywhere library provides a simple and effective way to encrypt and decrypt data using the Advanced Encryption Standard (AES) algorithm. It supports various platforms and programming languages, making it a versatile choice for securing environment variables.\
\
CSVbox supports **multiple encryption schemes** for securing sensitive importer data at rest.\
You can choose the mode that best fits your security or compliance requirements.

#### **Supported Encryption Types**

| Encryption Mode      | Notes                                                                                              |
| -------------------- | -------------------------------------------------------------------------------------------------- |
| AES-256-CBC (legacy) | Still supported, but recommended to move to newer CBC/GCM variants.                                |
| Updated AES-256-CBC  | Improved IV handling and hardened padding logic. Recommended if you prefer CBC mode.               |
| AES-256-GCM          | **Most secure**. Provides authenticity + integrity via GCM tags. Recommended for all new installs. |

#### Steps

* Install the [AES Everywhere library ](https://github.com/mervick/aes-everywhere)in your app.
* Go to your app admin dashboard > Account Menu > API Keys Page > Encryption Key section.
* Select the Encryption Type and Encryption Mode.
* Generate a secure Encryption Key&#x20;
* Use your Encryption Key and AES Everywhere library to encrypt the environment variables.

{% hint style="info" %}
Encrypt all the required environment variables in one single object.
{% endhint %}

Here is an example using JavaScript:

```javascript
const AES = require('aes-everywhere');
const secretKey = 'your-encryption-key';

const originalValue = {
                    base_url: "https://staging.mydomain.com",
                    authorized_domain: "https://staging.myapp.com",
                    user_id: "default123"
};

const encryptedValue = AES.encrypt(JSON.stringify(originalValue), secretKey);

console.log(`Encrypted Value: ${encryptedValue}`);
//encrypted value: U2FsdGVkX192dXI7yHGs/4Ed+xEC3ejXFINKO6Hufnc=

```

* Add the encrypted value to the **environment** object. The encrypted value should be passed to the **env\_encrypted** key.

{% tabs %}
{% tab title="Javascript" %}

```javascript
 let importer = new CSVBoxImporter("YOUR_LICENSE_KEY_HERE", {}, callback, 
    {
	lazy: true,
	environment: {
           env_name: 'staging',
           //encrypted values below
           env_encrypted: "U2FsdGVkX192dXI7yHGs/4Ed+xEC3ejXFINKO6Hufnc=",
	   module_id: 234234	                                  
        }
    });
```

{% endtab %}
{% endtabs %}

* The CSVbox importer will decrypt the environment variables using the same AES Everywhere library.


# Auth API

Authenticate API requests to CSVBox using a secure header-based scheme that validates both your API Key and Secret API Key.

### Overview

CSVBox requires **two authentication headers** for all protected API requests:

{% hint style="info" %}
WARNING

* Always send requests over **HTTPS**.
* Never include keys in query parameters or expose them in browser-side code.
* Store keys securely in your server environment.
  {% endhint %}

***

### Finding Your Keys

1. Log in to [CSVBox Dashboard](https://app.csvbox.io/).
2. Click your profile name → **Profile → API Keys** tab.
3. Copy your API and Secret keys.
4. If keys are missing or compromised, click **Regenerate Key**.

***

### Authentication Flow

1. Client sends a request to a protected endpoint with both headers.
2. CSVBox validates:
   * The API key exists and is active.
   * The Secret key matches the same account.
3. If valid → returns `200 OK` and requested data.
4. If invalid → returns an appropriate error code.

***

### Endpoint

## Verify Credentials API

<mark style="color:green;">`POST`</mark> `https://api.csvbox.io/1.1/auth`

You can verify your credentials using this endpoint.

**Headers**

| Name                    | Value                  |
| ----------------------- | ---------------------- |
| content-type            | `application/json`     |
| x-csvbox-api-key        | \<your-api-key>        |
| x-csvbox-secret-api-key | \<your-secret-api-key> |

**Body**

| Name   | Type   | Description      |
| ------ | ------ | ---------------- |
| `name` | string | Name of the user |
| `age`  | number | Age of the user  |

**Response**

{% tabs %}
{% tab title="200 OK" %}

```json
{
  "message": "successfully authenticated",
  "data": {
    "name": "John Doe",
    "email": "abc@xyz.com",
    "profile_photo_url": "<url_string>"
  }
}
```

{% endtab %}
{% endtabs %}

#### ❌ Error Responses

| Status                    | Error                        | Example                               |
| ------------------------- | ---------------------------- | ------------------------------------- |
| **400 Bad Request**       | Missing or malformed headers | `{ "errors": "bad_request" }`         |
| **401 Unauthorized**      | Invalid credentials          | `{ "errors": "invalid_credentials" }` |
| **403 Forbidden**         | Account lacks permission     | `{ "errors": "forbidden"}`            |
| **429 Too Many Requests** | Rate limit exceeded          | `{ "errors": "rate_limited"}`         |

***

### Security Best Practices

* Use **HTTPS (TLS)** exclusively.
* Send keys in **headers**, never in URLs.
* Mask secret fields in your UI (`type="password"`).
* Do **not log** raw keys—mask them (e.g., `****abcd`).
* Rotate and revoke keys regularly via dashboard.
* Store credentials as environment variables:

  ```bash
  CSVBOX_API_KEY=your_api_key
  CSVBOX_SECRET_API_KEY=your_secret_key
  ```
* For browser apps, always proxy API requests through your backend.

***

#### Example Requests

{% tabs %}
{% tab title="cURL" %}

```javascript
curl -i -X GET "https://api.csvbox.io/1.1/auth" \
  -H "Accept: application/json" \
  -H "x-csvbox-api-key: <your-api-key>" \
  -H "x-csvbox-secret-api-key: <your-secret-key>"
```

{% endtab %}

{% tab title="jQuery (for testing)" %}

```javascript
$.ajax({
  url: "https://api.csvbox.io/1.1/auth",
  headers: {
    "x-csvbox-api-key": "<api-key>",
    "x-csvbox-secret-api-key": "<secret-key>"
  },
  success: (data) => console.log("Authenticated:", data),
  error: (xhr) => console.error("Error:", xhr.status, xhr.responseJSON)
});
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$ch = curl_init("https://api.csvbox.io/1.1/auth");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Accept: application/json",
  "x-csvbox-api-key: " . getenv('CSVBOX_API_KEY'),
  "x-csvbox-secret-api-key: " . getenv('CSVBOX_SECRET_API_KEY')
]);

$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($code === 200) {
  echo "Authenticated successfully:\n" . $response;
} else {
  echo "Authentication failed (HTTP $code):\n" . $response;
}
```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
import axios from "axios";

async function testAuth() {
  try {
    const res = await axios.get("https://api.csvbox.io/1.1/auth", {
      headers: {
        "Accept": "application/json",
        "x-csvbox-api-key": process.env.CSVBOX_API_KEY,
        "x-csvbox-secret-api-key": process.env.CSVBOX_SECRET_API_KEY
      },
    });
    console.log("Authenticated:", res.data);
  } catch (err) {
    console.error("Error:", err.response?.data || err.message);
  }
}

testAuth();
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Never expose your secret keys in browser-side code.
{% endhint %}

***

### FAQ

**Can I send only the API key?**\
No. Both headers are required for authentication.

**Can I use query parameters for keys?**\
No. Query-based authentication is disabled for security reasons.

**Where should I store my keys?**\
Store them server-side in environment variables or a secrets manager.

**I lost my secret key. What now?**\
Regenerate it from the **API Keys** page.

***

### Troubleshooting & Support

If authentication fails:

1. Verify exact header names (`x-csvbox-api-key`, `x-csvbox-secret-api-key`).
2. Ensure your account and keys are active.
3. Confirm you’re using **HTTPS**.
4. If still failing, contact [support ](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur)with your request ID or timestamp.

***

### Summary

| Topic         | Details                                       |
| ------------- | --------------------------------------------- |
| Method        | Header-based (API Key + Secret Key)           |
| Headers       | `x-csvbox-api-key`, `x-csvbox-secret-api-key` |
| Protocol      | HTTPS only                                    |
| Auth Type     | Server-to-server                              |
| Test Endpoint | `GET https://api.csvbox.io/1.1/auth`          |

{% hint style="info" %}
Use this endpoint to verify your integration before making any other CSVBox API calls.
{% endhint %}


# Sheet API

## Create Sheet

> Creates a new sheet importer.

```json
{"openapi":"3.0.0","info":{"title":"CSVBox Sheets API Documentation","version":"1.2.0"},"tags":[{"name":"Sheets","description":"Sheets"}],"servers":[{"url":"https://api.csvbox.io/","description":"CSVBox Sheets API Server"}],"security":[{"ApiKeyAuth":[],"ApiSecretKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"x-csvbox-api-key","in":"header"}},"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be application/json","required":true,"schema":{"type":"string"}},"ContentTypeHeader":{"name":"Content-Type","in":"header","description":"Content type must be application/json","required":true,"schema":{"type":"string"}}},"schemas":{"CreateSheetRequest":{"required":["title"],"properties":{"title":{"type":"string"},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/Destinations"}},"sheet_columns":{"type":"array","items":{"$ref":"#/components/schemas/SheetColumn"}},"steps":{"$ref":"#/components/schemas/Steps"},"security_settings":{"$ref":"#/components/schemas/SecuritySettings"},"virtual_columns":{"description":"POST: every item listed is created. Omitting the key creates nothing.","type":"array","items":{"$ref":"#/components/schemas/VirtualColumn"},"maxItems":20},"validation_functions":{"description":"POST: every item listed is created. Omitting the key creates nothing. Two functions are shown: `check_email` needs nothing beyond the built-ins, while `check_date` declares `dayjs` — remove that dependency and its code is rejected with js_disallowed_global. That entry omits the optional `integrity` field so this body can be sent unmodified; in your own sheet add `\"integrity\": \"sha384-<digest published by the CDN>\"` beside the `url`, and add further scripts as further comma-separated objects in the same array (5 max).","type":"array","items":{"$ref":"#/components/schemas/ValidationFunction"},"maxItems":10},"data_transforms":{"description":"POST: every item listed is created. Omitting the key creates nothing.","type":"array","items":{"$ref":"#/components/schemas/DataTransform"},"maxItems":10}},"type":"object"},"Webhook":{"properties":{"import_complete":{"$ref":"#/components/schemas/WebhookImportComplete"}},"type":"object"},"WebhookImportComplete":{"properties":{"url":{"type":"string","format":"url"},"send_copy":{"description":"Send a copy of imported data to client","type":"boolean"},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"},"Header":{"required":["key","value"],"properties":{"key":{"description":"---------------------------------------------------------\nHEADER OBJECT\n---------------------------------------------------------","type":"string"},"value":{"type":"string"}},"type":"object"},"Destinations":{"properties":{"type":{"type":"string","enum":["webhook","testapi","none"]},"isActive":{"type":"boolean"},"settings":{"properties":{"method":{"type":"string","enum":["POST","PATCH"]},"url":{"type":"string","format":"url"},"post_data_format":{"type":"string","enum":["JSON","FORM_DATA","XML"]},"rows_per_chunk":{"type":"integer"},"request_type":{"type":"string","enum":["parallel","sequential"]},"server_side_validation":{"type":"boolean"},"allow_resubmit":{"type":"string","enum":["all_rows","error_rows_only"]},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"}},"type":"object"},"SheetColumn":{"required":["column_name"],"properties":{"column_name":{"type":"string"},"display_label":{"type":"string"},"info_hint":{"type":"string"},"matching_keywords":{"type":"string"},"type":{"type":"string","enum":["text","number","email","date","time","boolean","regex","ip","url","credit_card","phone_number","currency","list","dependent_list","dynamic_list","dependent_dynamic_list","multiselect_list","multiselect_dynamic_list"]},"default_value":{"type":"string"},"required":{"type":"boolean"},"read_only":{"type":"boolean"},"position":{"type":"integer","minimum":1},"validators":{"description":"Validators depend on the column type. See examples below for each type.","properties":{"min_length":{"description":"[text] Minimum character length","type":"integer"},"max_length":{"description":"[text] Maximum character length","type":"integer"},"min_value":{"description":"[number] Minimum numeric value","type":"number"},"max_value":{"description":"[number] Maximum numeric value","type":"number"},"format":{"description":"[date] Date format e.g. YYYY-MM-DD | [time] Time format e.g. HH:mm:ss","type":"string"},"expression":{"description":"[regex] Regular expression pattern","type":"string"},"error_message":{"description":"[regex] Custom error message","type":"string"},"version":{"description":"[ip] IP version","type":"string","enum":["ipv4","ipv6"]},"country_code":{"description":"[phone_number] Country code e.g. IN, US","type":"string"},"symbol":{"description":"[currency] Currency symbol","type":"string"},"require_symbol":{"description":"[currency] Whether symbol is required","type":"boolean"},"values":{"description":"[list] Array of ListItem objects (with dependents) | [multiselect_list] Array of strings","oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/ListItem"}},{"type":"array","items":{"type":"string"}}]},"case_sensitive":{"description":"[list, multiselect_list] Case-sensitive matching","type":"boolean"},"primary_column":{"description":"[dependent_list, dependent_dynamic_list] The column_name of the parent list column","type":"string"},"source_url":{"description":"[dynamic_list, multiselect_dynamic_list] URL to fetch list options from","type":"string"},"request_method":{"description":"[dynamic_list, multiselect_dynamic_list] HTTP method","type":"string","enum":["GET","POST"]},"request_headers":{"description":"[dynamic_list, multiselect_dynamic_list] Custom request headers","type":"array","items":{"$ref":"#/components/schemas/Header"}},"custom_user_attributes":{"description":"[dynamic_list] Include custom user attributes in request","type":"boolean"},"other_values":{"description":"[dynamic_list, multiselect_list, multiselect_dynamic_list] Allow values outside predefined list","type":"boolean"},"delimiter":{"description":"[multiselect_list, multiselect_dynamic_list] Delimiter for multiple values","type":"string"}},"type":"object"}},"type":"object"},"ListItem":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemChild"}}},"type":"object"},"ListItemChild":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemDependent"}}},"type":"object"},"ListItemDependent":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"}},"type":"object"},"Steps":{"properties":{"file_upload":{"properties":{"types":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"help":{"type":"string"},"sample_file_url":{"type":"string"},"size_mb":{"type":"integer"},"validation_msg":{"type":"string"},"copy_paste":{"type":"boolean"},"copy_delimiter":{"type":"string"},"show_upload":{"type":"boolean"},"worksheet_select":{"type":"boolean"},"description_option":{"type":"boolean"},"lang":{"type":"string"},"excel_date_format":{"type":"string"},"excel_date_custom":{"type":"string"},"hide_cancel":{"type":"boolean"},"split":{"description":"Split large files into multiple imports","type":"boolean"},"split_rows":{"description":"Rows per import when splitting (min 1)","type":"integer"},"split_confirm":{"description":"User confirmation required after each import: no=0, yes=1, no_if_no_errors=2","type":"string","enum":["no","yes","no_if_no_errors"]},"extract_types":{"description":"Allowed document extraction types (mapped to .pdf/.doc/images)","type":"array","items":{"type":"string","enum":["pdf","docs","images"]}},"page_limit":{"description":"Max pages allowed for extraction (null = no limit)","type":"integer","nullable":true}},"type":"object"},"select_header":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"default_row":{"type":"string"},"encoding_option":{"type":"boolean"},"encoding":{"type":"string"},"row_column_switch":{"type":"boolean"}},"type":"object"},"map_columns":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"mapping_mode":{"type":"string"},"bulk_cols_dd":{"type":"boolean"},"accept_unmapped":{"type":"boolean"},"accept_unmapped_select":{"type":"boolean"},"allow_zero_template":{"type":"boolean"},"ignore_cols":{"type":"boolean"},"ignore_cols_default":{"type":"boolean"},"user_keywords":{"type":"boolean"}},"type":"object"},"verify_data":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"row_display_mode":{"type":"string"},"export_option":{"type":"boolean"},"search_option":{"type":"boolean"},"ai_transform":{"type":"boolean"},"allow_new_rows":{"type":"boolean"},"rows_display":{"type":"integer"},"allow_invalid":{"type":"boolean"},"invalid_confirmation":{"type":"boolean"},"max_rows":{"type":"integer"},"allow_upload_if_max":{"type":"boolean"},"max_rows_msg":{"type":"string"},"min_rows":{"type":"integer"},"min_rows_msg":{"type":"string"}},"type":"object"},"results":{"properties":{"close_mode":{"type":"string"},"redirect_url":{"type":"string"},"success_type":{"type":"string"},"success_text":{"type":"string"},"success_method":{"type":"string"},"success_url":{"type":"string"},"failed_type":{"type":"string"},"failed_text":{"type":"string"},"failed_method":{"type":"string"},"failed_url":{"type":"string"},"show_resubmit":{"type":"boolean"},"show_error_text":{"type":"boolean"}},"type":"object"}},"type":"object"},"SecuritySettings":{"properties":{"region":{"type":"string","enum":["us","eu"]},"domains":{"type":"array","items":{"type":"string"}},"import_url_upload":{"type":"boolean"},"s3_upload":{"type":"boolean"}},"type":"object"},"VirtualColumn":{"description":"A computed column. Max 20 per sheet. `column_name` must not collide with a real sheet column.","required":["column_name"],"properties":{"column_name":{"type":"string","maxLength":190},"js_code":{"description":"Function body receiving a single `csvbox` argument and returning the computed value. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"description":"Defaults to true on API create — unlike the web UI, whose post-save toggle starts off.","type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. When true the item is deleted and every other field on it is ignored, including `js_code`.","type":"boolean"}},"type":"object"},"JsDependency":{"description":"A third-party script an item's `js_code` may use. The API never accepts raw HTML — send the URL and the server renders the <script src> tag. Migrating from the web UI: `js_dependency` markup is not a writable field. Full shape, all three fields, on any of the three collections: `\"dependencies\": [{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`. Only `url` is required; `globals` is what makes the library's name callable from `js_code`, and `integrity` is optional. Up to 5 dependencies per item — send them as separate objects in the array, comma-separated, one per script. The server renders each as `<script src=\"…\" integrity=\"…\" crossorigin=\"anonymous\"></script>`, in the order sent, so a library that must load before another goes first.","required":["url"],"properties":{"url":{"description":"HTTPS only. Host must be exactly cdn.jsdelivr.net, unpkg.com or cdnjs.cloudflare.com (no subdomains). Path must end in .js or .mjs. No query string, fragment, userinfo or port.","type":"string","format":"url","maxLength":512},"globals":{"description":"Names this script defines. Each is added to the allowed-identifier list for this item only — code referencing an undeclared global is rejected with js_disallowed_global. May not shadow a built-in or `csvbox`. Loading the script is not enough on its own: the code gate checks this list, so a name absent from it is refused whether or not the CDN would have defined it at runtime. Up to 5 names, each matching /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.","type":"array","items":{"type":"string"},"maxItems":5},"integrity":{"description":"Optional Subresource Integrity digest: sha256-, sha384- or sha512- followed by base64. When sent it is rendered as the tag's integrity attribute, so the browser refuses the script if the file does not hash to it. Take the digest from the CDN's own copy button (jsDelivr and cdnjs both publish one) — a digest that does not match the exact file at that URL stops the library loading, and the failure shows up in the importer at runtime, not as a 422 here. Pin an exact version in the URL when you use it: a floating version resolves to a different file later and invalidates the digest.","type":"string"}},"type":"object"},"ValidationFunction":{"description":"A custom validation rule. Max 10 per sheet.","required":["function_name"],"properties":{"function_name":{"type":"string","maxLength":190},"scope":{"description":"`column` runs over whole columns via csvbox.column[...]; `row` runs per row via csvbox.row[...]. A column-scoped item must name at least one entry across `columns` and `dynamic_columns`. On a PATCH updating an existing item, `scope`, `columns` and `dynamic_columns` preserve as a GROUP: send none of the three and the stored scoping is kept; send any one and all three are taken from the request, so `columns` alone also clears `dynamic_columns`.","type":"string","default":"column","enum":["column","row"]},"columns":{"description":"Sheet columns this function reads. Validated against the sheet as it stands AFTER this request, so one call may add a column and a function that uses it. A scope preserved by a PATCH that sent none of the three scope fields is written back as stored and is not re-checked.","type":"array","items":{"type":"string"}},"dynamic_columns":{"description":"Columns that only exist at import time and so cannot be checked against the sheet definition.","type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox` and returning an array of errors — empty means valid. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Scripts this item's `js_code` may use. The `check_date` example below is the worked case: `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"]}]`, plus an optional `integrity` digest per entry. Remove it and the same code is rejected with js_disallowed_global, which is the whole purpose of `globals`. Up to 5 entries, comma-separated in the array, loaded in the order sent.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"DataTransform":{"description":"A transform that rewrites values in place. Max 10 per sheet.","required":["transform_name"],"properties":{"transform_name":{"type":"string","maxLength":190},"scope":{"type":"string","default":"column","enum":["column","row"]},"run_at":{"description":"When the transform runs relative to validation.","type":"string","enum":["before_validation","after_validation"]},"columns":{"type":"array","items":{"type":"string"}},"dynamic_columns":{"type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox`, mutating it and returning it. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"SheetSuccessResponse":{"properties":{"status":{"type":"string"},"data":{"properties":{"sheet_license_key":{"type":"string"},"title":{"type":"string"},"virtual_columns":{"description":"Per-item outcome, present only when the request sent this collection. Additive — existing clients reading sheet_license_key and title are unaffected.","type":"array","items":{"properties":{"column_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"validation_functions":{"description":"Per-item outcome, present only when the request sent this collection. `unchanged` means the item was submitted but matched what was already stored, so no write happened.","type":"array","items":{"properties":{"function_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"data_transforms":{"description":"Per-item outcome, present only when the request sent this collection.","type":"array","items":{"properties":{"transform_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}}},"type":"object"}},"type":"object"},"ErrorResponse":{"properties":{"status":{"type":"string"},"errors":{"description":"Keys are dotted paths into the request body (for example `virtual_columns.0.js_code`), each mapping to a list of problems found at that path. Every failing item is reported in one response, so a caller pushing ten functions learns about all ten in a single round trip. Requests rejected by the JavaScript gate always use this form, and point into that item's own js_code: `line` is 1-based, `column` is 0-based. `identifier`, `line` and `column` are present only when the violation has one. Codes: js_code_too_long, js_forbidden_sequence, js_syntax_error, js_disallowed_global, js_forbidden_construct, js_duplicate_name, js_name_conflicts_column, js_unknown_column, js_too_many_items, js_budget_exceeded, js_name_ambiguous, js_missing_name, js_unknown_field, js_invalid_field, js_invalid_item, dependency_url_invalid, dependency_host_not_allowed, column_name_ambiguous.","type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"identifier":{"type":"string"},"line":{"type":"integer"},"column":{"type":"integer"}}}}}},"type":"object"}}},"paths":{"/1.1/sheet":{"post":{"tags":["Sheets"],"summary":"Create Sheet","description":"Creates a new sheet importer.","operationId":"createSheet","parameters":[{"$ref":"#/components/parameters/AcceptHeader"},{"$ref":"#/components/parameters/ContentTypeHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSheetRequest"}}}},"responses":{"201":{"description":"Sheet Created Successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SheetSuccessResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Update Sheet

> Fully updates an existing sheet. All provided fields will overwrite existing values.

```json
{"openapi":"3.0.0","info":{"title":"CSVBox Sheets API Documentation","version":"1.2.0"},"tags":[{"name":"Sheets","description":"Sheets"}],"servers":[{"url":"https://api.csvbox.io/","description":"CSVBox Sheets API Server"}],"security":[{"ApiKeyAuth":[],"ApiSecretKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"x-csvbox-api-key","in":"header"}},"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be application/json","required":true,"schema":{"type":"string"}},"ContentTypeHeader":{"name":"Content-Type","in":"header","description":"Content type must be application/json","required":true,"schema":{"type":"string"}}},"schemas":{"PutSheetRequest":{"properties":{"title":{"type":"string"},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/Destinations"}},"sheet_columns":{"type":"array","items":{"$ref":"#/components/schemas/SheetColumn"}},"steps":{"$ref":"#/components/schemas/Steps"},"security_settings":{"$ref":"#/components/schemas/SecuritySettings"},"virtual_columns":{"description":"PUT is AUTHORITATIVE for a collection it sends: items listed are created or updated in place, and any existing item this array does not name is DELETED. Sending [] deletes every virtual column on the sheet. Omitting the key entirely leaves the collection untouched. Use `_delete` under PATCH instead if you want targeted removal.","type":"array","items":{"$ref":"#/components/schemas/VirtualColumn"},"maxItems":20},"validation_functions":{"description":"PUT is AUTHORITATIVE: unlisted existing items are DELETED, [] empties the collection, an omitted key leaves it untouched. Two functions are shown: `check_email` needs nothing beyond the built-ins, while `check_date` declares `dayjs` — remove that dependency and its code is rejected with js_disallowed_global. That entry omits the optional `integrity` field so this body can be sent unmodified; in your own sheet add `\"integrity\": \"sha384-<digest published by the CDN>\"` beside the `url`, and add further scripts as further comma-separated objects in the same array (5 max).","type":"array","items":{"$ref":"#/components/schemas/ValidationFunction"},"maxItems":10},"data_transforms":{"description":"PUT is AUTHORITATIVE: unlisted existing items are DELETED, [] empties the collection, an omitted key leaves it untouched.","type":"array","items":{"$ref":"#/components/schemas/DataTransform"},"maxItems":10}},"type":"object"},"Webhook":{"properties":{"import_complete":{"$ref":"#/components/schemas/WebhookImportComplete"}},"type":"object"},"WebhookImportComplete":{"properties":{"url":{"type":"string","format":"url"},"send_copy":{"description":"Send a copy of imported data to client","type":"boolean"},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"},"Header":{"required":["key","value"],"properties":{"key":{"description":"---------------------------------------------------------\nHEADER OBJECT\n---------------------------------------------------------","type":"string"},"value":{"type":"string"}},"type":"object"},"Destinations":{"properties":{"type":{"type":"string","enum":["webhook","testapi","none"]},"isActive":{"type":"boolean"},"settings":{"properties":{"method":{"type":"string","enum":["POST","PATCH"]},"url":{"type":"string","format":"url"},"post_data_format":{"type":"string","enum":["JSON","FORM_DATA","XML"]},"rows_per_chunk":{"type":"integer"},"request_type":{"type":"string","enum":["parallel","sequential"]},"server_side_validation":{"type":"boolean"},"allow_resubmit":{"type":"string","enum":["all_rows","error_rows_only"]},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"}},"type":"object"},"SheetColumn":{"required":["column_name"],"properties":{"column_name":{"type":"string"},"display_label":{"type":"string"},"info_hint":{"type":"string"},"matching_keywords":{"type":"string"},"type":{"type":"string","enum":["text","number","email","date","time","boolean","regex","ip","url","credit_card","phone_number","currency","list","dependent_list","dynamic_list","dependent_dynamic_list","multiselect_list","multiselect_dynamic_list"]},"default_value":{"type":"string"},"required":{"type":"boolean"},"read_only":{"type":"boolean"},"position":{"type":"integer","minimum":1},"validators":{"description":"Validators depend on the column type. See examples below for each type.","properties":{"min_length":{"description":"[text] Minimum character length","type":"integer"},"max_length":{"description":"[text] Maximum character length","type":"integer"},"min_value":{"description":"[number] Minimum numeric value","type":"number"},"max_value":{"description":"[number] Maximum numeric value","type":"number"},"format":{"description":"[date] Date format e.g. YYYY-MM-DD | [time] Time format e.g. HH:mm:ss","type":"string"},"expression":{"description":"[regex] Regular expression pattern","type":"string"},"error_message":{"description":"[regex] Custom error message","type":"string"},"version":{"description":"[ip] IP version","type":"string","enum":["ipv4","ipv6"]},"country_code":{"description":"[phone_number] Country code e.g. IN, US","type":"string"},"symbol":{"description":"[currency] Currency symbol","type":"string"},"require_symbol":{"description":"[currency] Whether symbol is required","type":"boolean"},"values":{"description":"[list] Array of ListItem objects (with dependents) | [multiselect_list] Array of strings","oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/ListItem"}},{"type":"array","items":{"type":"string"}}]},"case_sensitive":{"description":"[list, multiselect_list] Case-sensitive matching","type":"boolean"},"primary_column":{"description":"[dependent_list, dependent_dynamic_list] The column_name of the parent list column","type":"string"},"source_url":{"description":"[dynamic_list, multiselect_dynamic_list] URL to fetch list options from","type":"string"},"request_method":{"description":"[dynamic_list, multiselect_dynamic_list] HTTP method","type":"string","enum":["GET","POST"]},"request_headers":{"description":"[dynamic_list, multiselect_dynamic_list] Custom request headers","type":"array","items":{"$ref":"#/components/schemas/Header"}},"custom_user_attributes":{"description":"[dynamic_list] Include custom user attributes in request","type":"boolean"},"other_values":{"description":"[dynamic_list, multiselect_list, multiselect_dynamic_list] Allow values outside predefined list","type":"boolean"},"delimiter":{"description":"[multiselect_list, multiselect_dynamic_list] Delimiter for multiple values","type":"string"}},"type":"object"}},"type":"object"},"ListItem":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemChild"}}},"type":"object"},"ListItemChild":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemDependent"}}},"type":"object"},"ListItemDependent":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"}},"type":"object"},"Steps":{"properties":{"file_upload":{"properties":{"types":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"help":{"type":"string"},"sample_file_url":{"type":"string"},"size_mb":{"type":"integer"},"validation_msg":{"type":"string"},"copy_paste":{"type":"boolean"},"copy_delimiter":{"type":"string"},"show_upload":{"type":"boolean"},"worksheet_select":{"type":"boolean"},"description_option":{"type":"boolean"},"lang":{"type":"string"},"excel_date_format":{"type":"string"},"excel_date_custom":{"type":"string"},"hide_cancel":{"type":"boolean"},"split":{"description":"Split large files into multiple imports","type":"boolean"},"split_rows":{"description":"Rows per import when splitting (min 1)","type":"integer"},"split_confirm":{"description":"User confirmation required after each import: no=0, yes=1, no_if_no_errors=2","type":"string","enum":["no","yes","no_if_no_errors"]},"extract_types":{"description":"Allowed document extraction types (mapped to .pdf/.doc/images)","type":"array","items":{"type":"string","enum":["pdf","docs","images"]}},"page_limit":{"description":"Max pages allowed for extraction (null = no limit)","type":"integer","nullable":true}},"type":"object"},"select_header":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"default_row":{"type":"string"},"encoding_option":{"type":"boolean"},"encoding":{"type":"string"},"row_column_switch":{"type":"boolean"}},"type":"object"},"map_columns":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"mapping_mode":{"type":"string"},"bulk_cols_dd":{"type":"boolean"},"accept_unmapped":{"type":"boolean"},"accept_unmapped_select":{"type":"boolean"},"allow_zero_template":{"type":"boolean"},"ignore_cols":{"type":"boolean"},"ignore_cols_default":{"type":"boolean"},"user_keywords":{"type":"boolean"}},"type":"object"},"verify_data":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"row_display_mode":{"type":"string"},"export_option":{"type":"boolean"},"search_option":{"type":"boolean"},"ai_transform":{"type":"boolean"},"allow_new_rows":{"type":"boolean"},"rows_display":{"type":"integer"},"allow_invalid":{"type":"boolean"},"invalid_confirmation":{"type":"boolean"},"max_rows":{"type":"integer"},"allow_upload_if_max":{"type":"boolean"},"max_rows_msg":{"type":"string"},"min_rows":{"type":"integer"},"min_rows_msg":{"type":"string"}},"type":"object"},"results":{"properties":{"close_mode":{"type":"string"},"redirect_url":{"type":"string"},"success_type":{"type":"string"},"success_text":{"type":"string"},"success_method":{"type":"string"},"success_url":{"type":"string"},"failed_type":{"type":"string"},"failed_text":{"type":"string"},"failed_method":{"type":"string"},"failed_url":{"type":"string"},"show_resubmit":{"type":"boolean"},"show_error_text":{"type":"boolean"}},"type":"object"}},"type":"object"},"SecuritySettings":{"properties":{"region":{"type":"string","enum":["us","eu"]},"domains":{"type":"array","items":{"type":"string"}},"import_url_upload":{"type":"boolean"},"s3_upload":{"type":"boolean"}},"type":"object"},"VirtualColumn":{"description":"A computed column. Max 20 per sheet. `column_name` must not collide with a real sheet column.","required":["column_name"],"properties":{"column_name":{"type":"string","maxLength":190},"js_code":{"description":"Function body receiving a single `csvbox` argument and returning the computed value. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"description":"Defaults to true on API create — unlike the web UI, whose post-save toggle starts off.","type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. When true the item is deleted and every other field on it is ignored, including `js_code`.","type":"boolean"}},"type":"object"},"JsDependency":{"description":"A third-party script an item's `js_code` may use. The API never accepts raw HTML — send the URL and the server renders the <script src> tag. Migrating from the web UI: `js_dependency` markup is not a writable field. Full shape, all three fields, on any of the three collections: `\"dependencies\": [{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`. Only `url` is required; `globals` is what makes the library's name callable from `js_code`, and `integrity` is optional. Up to 5 dependencies per item — send them as separate objects in the array, comma-separated, one per script. The server renders each as `<script src=\"…\" integrity=\"…\" crossorigin=\"anonymous\"></script>`, in the order sent, so a library that must load before another goes first.","required":["url"],"properties":{"url":{"description":"HTTPS only. Host must be exactly cdn.jsdelivr.net, unpkg.com or cdnjs.cloudflare.com (no subdomains). Path must end in .js or .mjs. No query string, fragment, userinfo or port.","type":"string","format":"url","maxLength":512},"globals":{"description":"Names this script defines. Each is added to the allowed-identifier list for this item only — code referencing an undeclared global is rejected with js_disallowed_global. May not shadow a built-in or `csvbox`. Loading the script is not enough on its own: the code gate checks this list, so a name absent from it is refused whether or not the CDN would have defined it at runtime. Up to 5 names, each matching /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.","type":"array","items":{"type":"string"},"maxItems":5},"integrity":{"description":"Optional Subresource Integrity digest: sha256-, sha384- or sha512- followed by base64. When sent it is rendered as the tag's integrity attribute, so the browser refuses the script if the file does not hash to it. Take the digest from the CDN's own copy button (jsDelivr and cdnjs both publish one) — a digest that does not match the exact file at that URL stops the library loading, and the failure shows up in the importer at runtime, not as a 422 here. Pin an exact version in the URL when you use it: a floating version resolves to a different file later and invalidates the digest.","type":"string"}},"type":"object"},"ValidationFunction":{"description":"A custom validation rule. Max 10 per sheet.","required":["function_name"],"properties":{"function_name":{"type":"string","maxLength":190},"scope":{"description":"`column` runs over whole columns via csvbox.column[...]; `row` runs per row via csvbox.row[...]. A column-scoped item must name at least one entry across `columns` and `dynamic_columns`. On a PATCH updating an existing item, `scope`, `columns` and `dynamic_columns` preserve as a GROUP: send none of the three and the stored scoping is kept; send any one and all three are taken from the request, so `columns` alone also clears `dynamic_columns`.","type":"string","default":"column","enum":["column","row"]},"columns":{"description":"Sheet columns this function reads. Validated against the sheet as it stands AFTER this request, so one call may add a column and a function that uses it. A scope preserved by a PATCH that sent none of the three scope fields is written back as stored and is not re-checked.","type":"array","items":{"type":"string"}},"dynamic_columns":{"description":"Columns that only exist at import time and so cannot be checked against the sheet definition.","type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox` and returning an array of errors — empty means valid. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Scripts this item's `js_code` may use. The `check_date` example below is the worked case: `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"]}]`, plus an optional `integrity` digest per entry. Remove it and the same code is rejected with js_disallowed_global, which is the whole purpose of `globals`. Up to 5 entries, comma-separated in the array, loaded in the order sent.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"DataTransform":{"description":"A transform that rewrites values in place. Max 10 per sheet.","required":["transform_name"],"properties":{"transform_name":{"type":"string","maxLength":190},"scope":{"type":"string","default":"column","enum":["column","row"]},"run_at":{"description":"When the transform runs relative to validation.","type":"string","enum":["before_validation","after_validation"]},"columns":{"type":"array","items":{"type":"string"}},"dynamic_columns":{"type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox`, mutating it and returning it. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"SheetSuccessResponse":{"properties":{"status":{"type":"string"},"data":{"properties":{"sheet_license_key":{"type":"string"},"title":{"type":"string"},"virtual_columns":{"description":"Per-item outcome, present only when the request sent this collection. Additive — existing clients reading sheet_license_key and title are unaffected.","type":"array","items":{"properties":{"column_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"validation_functions":{"description":"Per-item outcome, present only when the request sent this collection. `unchanged` means the item was submitted but matched what was already stored, so no write happened.","type":"array","items":{"properties":{"function_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"data_transforms":{"description":"Per-item outcome, present only when the request sent this collection.","type":"array","items":{"properties":{"transform_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}}},"type":"object"}},"type":"object"},"ErrorResponse":{"properties":{"status":{"type":"string"},"errors":{"description":"Keys are dotted paths into the request body (for example `virtual_columns.0.js_code`), each mapping to a list of problems found at that path. Every failing item is reported in one response, so a caller pushing ten functions learns about all ten in a single round trip. Requests rejected by the JavaScript gate always use this form, and point into that item's own js_code: `line` is 1-based, `column` is 0-based. `identifier`, `line` and `column` are present only when the violation has one. Codes: js_code_too_long, js_forbidden_sequence, js_syntax_error, js_disallowed_global, js_forbidden_construct, js_duplicate_name, js_name_conflicts_column, js_unknown_column, js_too_many_items, js_budget_exceeded, js_name_ambiguous, js_missing_name, js_unknown_field, js_invalid_field, js_invalid_item, dependency_url_invalid, dependency_host_not_allowed, column_name_ambiguous.","type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"identifier":{"type":"string"},"line":{"type":"integer"},"column":{"type":"integer"}}}}}},"type":"object"}}},"paths":{"/1.1/sheet/{sheet_license_key}":{"put":{"tags":["Sheets"],"summary":"Update Sheet","description":"Fully updates an existing sheet. All provided fields will overwrite existing values.","operationId":"updateSheet","parameters":[{"name":"sheet_license_key","in":"path","description":"Sheet License Key","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/AcceptHeader"},{"$ref":"#/components/parameters/ContentTypeHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutSheetRequest"}}}},"responses":{"200":{"description":"Sheet Updated Successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SheetSuccessResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sheet Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Partially Update Sheet

> Partially updates an existing sheet. Only provided fields will be updated. Fields not included will remain unchanged.

```json
{"openapi":"3.0.0","info":{"title":"CSVBox Sheets API Documentation","version":"1.2.0"},"tags":[{"name":"Sheets","description":"Sheets"}],"servers":[{"url":"https://api.csvbox.io/","description":"CSVBox Sheets API Server"}],"security":[{"ApiKeyAuth":[],"ApiSecretKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","name":"x-csvbox-api-key","in":"header"}},"parameters":{"AcceptHeader":{"name":"Accept","in":"header","description":"Must be application/json","required":true,"schema":{"type":"string"}},"ContentTypeHeader":{"name":"Content-Type","in":"header","description":"Content type must be application/json","required":true,"schema":{"type":"string"}}},"schemas":{"PatchSheetRequest":{"properties":{"title":{"type":"string"},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/Destinations"}},"sheet_columns":{"type":"array","items":{"$ref":"#/components/schemas/SheetColumn"}},"steps":{"$ref":"#/components/schemas/Steps"},"security_settings":{"$ref":"#/components/schemas/SecuritySettings"},"virtual_columns":{"description":"PATCH MERGES: only the items you list are touched. Existing items you do not name are left alone — nothing is deleted implicitly. Sending [] is a no-op, not a clear. To remove one item send {\"column_name\": \"old_one\", \"_delete\": true}; every other field on a _delete item is ignored, so js_code is not required. It merges at FIELD level too: on an item that already exists every field you omit keeps its stored value, so {\"function_name\": \"check_email\", \"active\": false} disables that function without re-sending its code, columns or dependencies.","type":"array","items":{"$ref":"#/components/schemas/VirtualColumn"},"maxItems":20},"validation_functions":{"description":"PATCH MERGES: unlisted items are untouched, [] is a no-op. Delete one with {\"function_name\": \"old_one\", \"_delete\": true}. Two functions are shown: `check_email` needs nothing beyond the built-ins, while `check_date` declares `dayjs` — remove that dependency and its code is rejected with js_disallowed_global. That entry omits the optional `integrity` field so this body can be sent unmodified; in your own sheet add `\"integrity\": \"sha384-<digest published by the CDN>\"` beside the `url`, and add further scripts as further comma-separated objects in the same array (5 max).","type":"array","items":{"$ref":"#/components/schemas/ValidationFunction"},"maxItems":10},"data_transforms":{"description":"PATCH MERGES: unlisted items are untouched, [] is a no-op. Delete one with {\"transform_name\": \"old_one\", \"_delete\": true}.","type":"array","items":{"$ref":"#/components/schemas/DataTransform"},"maxItems":10}},"type":"object"},"Webhook":{"properties":{"import_complete":{"$ref":"#/components/schemas/WebhookImportComplete"}},"type":"object"},"WebhookImportComplete":{"properties":{"url":{"type":"string","format":"url"},"send_copy":{"description":"Send a copy of imported data to client","type":"boolean"},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"},"Header":{"required":["key","value"],"properties":{"key":{"description":"---------------------------------------------------------\nHEADER OBJECT\n---------------------------------------------------------","type":"string"},"value":{"type":"string"}},"type":"object"},"Destinations":{"properties":{"type":{"type":"string","enum":["webhook","testapi","none"]},"isActive":{"type":"boolean"},"settings":{"properties":{"method":{"type":"string","enum":["POST","PATCH"]},"url":{"type":"string","format":"url"},"post_data_format":{"type":"string","enum":["JSON","FORM_DATA","XML"]},"rows_per_chunk":{"type":"integer"},"request_type":{"type":"string","enum":["parallel","sequential"]},"server_side_validation":{"type":"boolean"},"allow_resubmit":{"type":"string","enum":["all_rows","error_rows_only"]},"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/Header"}}},"type":"object"}},"type":"object"},"SheetColumn":{"required":["column_name"],"properties":{"column_name":{"type":"string"},"display_label":{"type":"string"},"info_hint":{"type":"string"},"matching_keywords":{"type":"string"},"type":{"type":"string","enum":["text","number","email","date","time","boolean","regex","ip","url","credit_card","phone_number","currency","list","dependent_list","dynamic_list","dependent_dynamic_list","multiselect_list","multiselect_dynamic_list"]},"default_value":{"type":"string"},"required":{"type":"boolean"},"read_only":{"type":"boolean"},"position":{"type":"integer","minimum":1},"validators":{"description":"Validators depend on the column type. See examples below for each type.","properties":{"min_length":{"description":"[text] Minimum character length","type":"integer"},"max_length":{"description":"[text] Maximum character length","type":"integer"},"min_value":{"description":"[number] Minimum numeric value","type":"number"},"max_value":{"description":"[number] Maximum numeric value","type":"number"},"format":{"description":"[date] Date format e.g. YYYY-MM-DD | [time] Time format e.g. HH:mm:ss","type":"string"},"expression":{"description":"[regex] Regular expression pattern","type":"string"},"error_message":{"description":"[regex] Custom error message","type":"string"},"version":{"description":"[ip] IP version","type":"string","enum":["ipv4","ipv6"]},"country_code":{"description":"[phone_number] Country code e.g. IN, US","type":"string"},"symbol":{"description":"[currency] Currency symbol","type":"string"},"require_symbol":{"description":"[currency] Whether symbol is required","type":"boolean"},"values":{"description":"[list] Array of ListItem objects (with dependents) | [multiselect_list] Array of strings","oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/ListItem"}},{"type":"array","items":{"type":"string"}}]},"case_sensitive":{"description":"[list, multiselect_list] Case-sensitive matching","type":"boolean"},"primary_column":{"description":"[dependent_list, dependent_dynamic_list] The column_name of the parent list column","type":"string"},"source_url":{"description":"[dynamic_list, multiselect_dynamic_list] URL to fetch list options from","type":"string"},"request_method":{"description":"[dynamic_list, multiselect_dynamic_list] HTTP method","type":"string","enum":["GET","POST"]},"request_headers":{"description":"[dynamic_list, multiselect_dynamic_list] Custom request headers","type":"array","items":{"$ref":"#/components/schemas/Header"}},"custom_user_attributes":{"description":"[dynamic_list] Include custom user attributes in request","type":"boolean"},"other_values":{"description":"[dynamic_list, multiselect_list, multiselect_dynamic_list] Allow values outside predefined list","type":"boolean"},"delimiter":{"description":"[multiselect_list, multiselect_dynamic_list] Delimiter for multiple values","type":"string"}},"type":"object"}},"type":"object"},"ListItem":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemChild"}}},"type":"object"},"ListItemChild":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"},"dependents":{"type":"array","items":{"$ref":"#/components/schemas/ListItemDependent"}}},"type":"object"},"ListItemDependent":{"properties":{"value":{"type":"string"},"display_label":{"type":"string"}},"type":"object"},"Steps":{"properties":{"file_upload":{"properties":{"types":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"help":{"type":"string"},"sample_file_url":{"type":"string"},"size_mb":{"type":"integer"},"validation_msg":{"type":"string"},"copy_paste":{"type":"boolean"},"copy_delimiter":{"type":"string"},"show_upload":{"type":"boolean"},"worksheet_select":{"type":"boolean"},"description_option":{"type":"boolean"},"lang":{"type":"string"},"excel_date_format":{"type":"string"},"excel_date_custom":{"type":"string"},"hide_cancel":{"type":"boolean"},"split":{"description":"Split large files into multiple imports","type":"boolean"},"split_rows":{"description":"Rows per import when splitting (min 1)","type":"integer"},"split_confirm":{"description":"User confirmation required after each import: no=0, yes=1, no_if_no_errors=2","type":"string","enum":["no","yes","no_if_no_errors"]},"extract_types":{"description":"Allowed document extraction types (mapped to .pdf/.doc/images)","type":"array","items":{"type":"string","enum":["pdf","docs","images"]}},"page_limit":{"description":"Max pages allowed for extraction (null = no limit)","type":"integer","nullable":true}},"type":"object"},"select_header":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"default_row":{"type":"string"},"encoding_option":{"type":"boolean"},"encoding":{"type":"string"},"row_column_switch":{"type":"boolean"}},"type":"object"},"map_columns":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"mapping_mode":{"type":"string"},"bulk_cols_dd":{"type":"boolean"},"accept_unmapped":{"type":"boolean"},"accept_unmapped_select":{"type":"boolean"},"allow_zero_template":{"type":"boolean"},"ignore_cols":{"type":"boolean"},"ignore_cols_default":{"type":"boolean"},"user_keywords":{"type":"boolean"}},"type":"object"},"verify_data":{"properties":{"skip":{"type":"boolean"},"help":{"type":"string"},"row_display_mode":{"type":"string"},"export_option":{"type":"boolean"},"search_option":{"type":"boolean"},"ai_transform":{"type":"boolean"},"allow_new_rows":{"type":"boolean"},"rows_display":{"type":"integer"},"allow_invalid":{"type":"boolean"},"invalid_confirmation":{"type":"boolean"},"max_rows":{"type":"integer"},"allow_upload_if_max":{"type":"boolean"},"max_rows_msg":{"type":"string"},"min_rows":{"type":"integer"},"min_rows_msg":{"type":"string"}},"type":"object"},"results":{"properties":{"close_mode":{"type":"string"},"redirect_url":{"type":"string"},"success_type":{"type":"string"},"success_text":{"type":"string"},"success_method":{"type":"string"},"success_url":{"type":"string"},"failed_type":{"type":"string"},"failed_text":{"type":"string"},"failed_method":{"type":"string"},"failed_url":{"type":"string"},"show_resubmit":{"type":"boolean"},"show_error_text":{"type":"boolean"}},"type":"object"}},"type":"object"},"SecuritySettings":{"properties":{"region":{"type":"string","enum":["us","eu"]},"domains":{"type":"array","items":{"type":"string"}},"import_url_upload":{"type":"boolean"},"s3_upload":{"type":"boolean"}},"type":"object"},"VirtualColumn":{"description":"A computed column. Max 20 per sheet. `column_name` must not collide with a real sheet column.","required":["column_name"],"properties":{"column_name":{"type":"string","maxLength":190},"js_code":{"description":"Function body receiving a single `csvbox` argument and returning the computed value. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"description":"Defaults to true on API create — unlike the web UI, whose post-save toggle starts off.","type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. When true the item is deleted and every other field on it is ignored, including `js_code`.","type":"boolean"}},"type":"object"},"JsDependency":{"description":"A third-party script an item's `js_code` may use. The API never accepts raw HTML — send the URL and the server renders the <script src> tag. Migrating from the web UI: `js_dependency` markup is not a writable field. Full shape, all three fields, on any of the three collections: `\"dependencies\": [{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`. Only `url` is required; `globals` is what makes the library's name callable from `js_code`, and `integrity` is optional. Up to 5 dependencies per item — send them as separate objects in the array, comma-separated, one per script. The server renders each as `<script src=\"…\" integrity=\"…\" crossorigin=\"anonymous\"></script>`, in the order sent, so a library that must load before another goes first.","required":["url"],"properties":{"url":{"description":"HTTPS only. Host must be exactly cdn.jsdelivr.net, unpkg.com or cdnjs.cloudflare.com (no subdomains). Path must end in .js or .mjs. No query string, fragment, userinfo or port.","type":"string","format":"url","maxLength":512},"globals":{"description":"Names this script defines. Each is added to the allowed-identifier list for this item only — code referencing an undeclared global is rejected with js_disallowed_global. May not shadow a built-in or `csvbox`. Loading the script is not enough on its own: the code gate checks this list, so a name absent from it is refused whether or not the CDN would have defined it at runtime. Up to 5 names, each matching /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.","type":"array","items":{"type":"string"},"maxItems":5},"integrity":{"description":"Optional Subresource Integrity digest: sha256-, sha384- or sha512- followed by base64. When sent it is rendered as the tag's integrity attribute, so the browser refuses the script if the file does not hash to it. Take the digest from the CDN's own copy button (jsDelivr and cdnjs both publish one) — a digest that does not match the exact file at that URL stops the library loading, and the failure shows up in the importer at runtime, not as a 422 here. Pin an exact version in the URL when you use it: a floating version resolves to a different file later and invalidates the digest.","type":"string"}},"type":"object"},"ValidationFunction":{"description":"A custom validation rule. Max 10 per sheet.","required":["function_name"],"properties":{"function_name":{"type":"string","maxLength":190},"scope":{"description":"`column` runs over whole columns via csvbox.column[...]; `row` runs per row via csvbox.row[...]. A column-scoped item must name at least one entry across `columns` and `dynamic_columns`. On a PATCH updating an existing item, `scope`, `columns` and `dynamic_columns` preserve as a GROUP: send none of the three and the stored scoping is kept; send any one and all three are taken from the request, so `columns` alone also clears `dynamic_columns`.","type":"string","default":"column","enum":["column","row"]},"columns":{"description":"Sheet columns this function reads. Validated against the sheet as it stands AFTER this request, so one call may add a column and a function that uses it. A scope preserved by a PATCH that sent none of the three scope fields is written back as stored and is not re-checked.","type":"array","items":{"type":"string"}},"dynamic_columns":{"description":"Columns that only exist at import time and so cannot be checked against the sheet definition.","type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox` and returning an array of errors — empty means valid. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Scripts this item's `js_code` may use. The `check_date` example below is the worked case: `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"]}]`, plus an optional `integrity` digest per entry. Remove it and the same code is rejected with js_disallowed_global, which is the whole purpose of `globals`. Up to 5 entries, comma-separated in the array, loaded in the order sent.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"DataTransform":{"description":"A transform that rewrites values in place. Max 10 per sheet.","required":["transform_name"],"properties":{"transform_name":{"type":"string","maxLength":190},"scope":{"type":"string","default":"column","enum":["column","row"]},"run_at":{"description":"When the transform runs relative to validation.","type":"string","enum":["before_validation","after_validation"]},"columns":{"type":"array","items":{"type":"string"}},"dynamic_columns":{"type":"array","items":{"type":"string"}},"js_code":{"description":"Function body receiving `csvbox`, mutating it and returning it. Required unless `_delete` is true, or a PATCH is updating an item that already exists — an omitted `js_code` then keeps the stored snippet. Sending an empty string, null or whitespace is refused with js_syntax_error: preservation is triggered by absence, not emptiness.","type":"string","maxLength":32768},"active":{"type":"boolean","default":true},"dependencies":{"description":"Supported here as on every collection. Empty in this example because the snippet above needs only `csvbox` and built-ins — see the `check_date` validation function for a worked dependency. To use one here, send `[{\"url\": \"https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js\", \"globals\": [\"dayjs\"], \"integrity\": \"sha384-…\"}]`; `globals` and `integrity` are optional, and further scripts are further objects in the same array. See the JsDependency schema for the host allowlist and per-field rules.","type":"array","items":{"$ref":"#/components/schemas/JsDependency"},"maxItems":5},"_delete":{"description":"PATCH only. See VirtualColumn._delete.","type":"boolean"}},"type":"object"},"SheetSuccessResponse":{"properties":{"status":{"type":"string"},"data":{"properties":{"sheet_license_key":{"type":"string"},"title":{"type":"string"},"virtual_columns":{"description":"Per-item outcome, present only when the request sent this collection. Additive — existing clients reading sheet_license_key and title are unaffected.","type":"array","items":{"properties":{"column_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"validation_functions":{"description":"Per-item outcome, present only when the request sent this collection. `unchanged` means the item was submitted but matched what was already stored, so no write happened.","type":"array","items":{"properties":{"function_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}},"data_transforms":{"description":"Per-item outcome, present only when the request sent this collection.","type":"array","items":{"properties":{"transform_name":{"type":"string"},"result":{"type":"string","enum":["created","updated","unchanged","deleted"]}},"type":"object"}}},"type":"object"}},"type":"object"},"ErrorResponse":{"properties":{"status":{"type":"string"},"errors":{"description":"Keys are dotted paths into the request body (for example `virtual_columns.0.js_code`), each mapping to a list of problems found at that path. Every failing item is reported in one response, so a caller pushing ten functions learns about all ten in a single round trip. Requests rejected by the JavaScript gate always use this form, and point into that item's own js_code: `line` is 1-based, `column` is 0-based. `identifier`, `line` and `column` are present only when the violation has one. Codes: js_code_too_long, js_forbidden_sequence, js_syntax_error, js_disallowed_global, js_forbidden_construct, js_duplicate_name, js_name_conflicts_column, js_unknown_column, js_too_many_items, js_budget_exceeded, js_name_ambiguous, js_missing_name, js_unknown_field, js_invalid_field, js_invalid_item, dependency_url_invalid, dependency_host_not_allowed, column_name_ambiguous.","type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"identifier":{"type":"string"},"line":{"type":"integer"},"column":{"type":"integer"}}}}}},"type":"object"}}},"paths":{"/1.1/sheet/{sheet_license_key}":{"patch":{"tags":["Sheets"],"summary":"Partially Update Sheet","description":"Partially updates an existing sheet. Only provided fields will be updated. Fields not included will remain unchanged.","operationId":"patchSheet","parameters":[{"name":"sheet_license_key","in":"path","description":"Sheet License Key","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/AcceptHeader"},{"$ref":"#/components/parameters/ContentTypeHeader"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchSheetRequest"}}}},"responses":{"200":{"description":"Sheet Partially Updated Successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SheetSuccessResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sheet Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```


# Data Destinations

This document defines the different destinations where the importer can push the data uploaded by the users. The following is the list of supported destinations.

1. [None](#none)
2. [Test API](#test-api)
3. [API](#api-webhook)
4. [Amazon S3](#amazon-s3)
5. [MySQL](#mysql)
6. [PostgreSQL](#postgresql)
7. [SQL Server ](#sql-server)
8. [Google Sheet](#google-sheets)
9. [FTP Server](#ftp-server)
10. [Bubble.io](#bubble)
11. [Airtable](#airtable)
12. [Zapier](#zapier)
13. [Notion](#notion)
14. [Webflow](#webflow)
15. [Softr](/destinations/softr)
16. [MongoDB](#mongodb)
17. [Google BigQuery](/destinations/google-bigquery)
18. [Supabase](/destinations/supabase)
19. [n8n](/destinations/n8n)
20. [Pipedream](/destinations/pipedream)
21. [Automation Platforms](/destinations/automation-platforms)
22. [Private Mode](/destinations/private-mode)
23. [Open API](/destinations/openapi)
24. [Neon](/destinations/neon)

{% hint style="info" %}
At a time only one destination can be selected per sheet.
{% endhint %}

## None

The user-uploaded data will not be pushed anywhere. The files will be, however, available for download via the csvbox.io admin.

## Test API

A lightweight destination to help you validate your CSVBox setup quickly—before wiring your own backend.

When selected, every successful import posts the parsed spreadsheet data to a **unique HTTPS URL** hosted at <https://webhooks.csvbox.io/>. Example:

```html
https://webhooks.csvbox.io/b/5f3b0901-1c32-4d83-9fdd-a454e56e1938
```

Use it to confirm payload shape, headers, and delivery behavior during initial integration.

{% hint style="info" %}
**Important:** The Test API is designed **for initial testing only**. Do not use it for production data flows.
{% endhint %}

### When to use it

* You’re integrating CSVBox for the first time and want to **see the JSON** CSVBox will send.
* You need to **confirm columns, data types, and null handling** after mapper/validators run.
* You want a **no-setup** endpoint to verify end-to-end delivery before connecting your real destination.

## API / Webhook

The data will be pushed to a webhook endpoint as configured in the sheet settings. You can choose between JSON, XML, and FORM Data formats for receiving data to your webhook. The data will be pushed in chunks of rows. The number of rows per chunk can be configured in the sheet settings.

#### Sample JSON POST to your API:

```javascript
[
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 1,
    "total_rows": 5,
    "env_name": "default", 
    "original_filename": "products01_24.csv",
    "row_data": {
          "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
          "SKU": "AS-100221",
          "Price": "33.00",
          "Quantity": "3",
          "Image URL": "https://cdn.shopify.com/s/files/1/1491/9536/products/31jJOj1DS5L_070b4893-b7af-482f-8a15-d40f5e06760d.jpg?v=1521803806"
    },
    "custom_fields": {
      "user_id": "1a2b3c4d5e6f",
      "team_id": "sales2"
    }
  },
  {
    "import_id": 79418895,
    "sheet_id": 55,
    "sheet_name": "Products",
    "row_number": 2,
    "total_rows": 5,
    "env_name": "default", 
    "original_filename": "products01_24.csv",
    "row_data":{
          "Name": "EPower Technology EP-600PM Power Supply 600W ATX12V 2.3 Single 120mm Cooling Fan Bare",
          "SKU": "AS-103824",
          "Price": "95.35",
          "Quantity": "8",
          "Image URL": "https://cdn.shopify.com/s/files/1/1491/9536/products/71pRC5VjF-L_8f840eb9-6a47-407f-999c-490f7814159d.jpg?v=1521803806"
        },
    "custom_fields": {
      "user_id": "1a2b3c4d5e6f"
      "team_id": "sales2"
    }
  },
]
```

{% hint style="info" %}
The data will come in as HTTP POST requests. Each request will have an array of rows based on the chunk size defined in the sheet settings. You can set the chunk size to 1 to receive 1 record per HTTP request.

If the row count is greater than 10,000 then the chunk size will default to 1000. A large row count having a small chunk size increases the processing time significantly.
{% endhint %}

#### Request Type

To push the data to the destination the webhook can be called in two modes:

1. **Sequential** - Webhook APIs will be invoked in a sequential order one after another.
2. **Parallel** (Default) - Multiple webhook APIs will be invoked concurrently. It means your application will receive chunks of rows in parallel. This method reduces overall import time. However, your application will need to manage the order of data based on the row\_number attribute.

If you want to jump in and get started, we recommend testing using [webhook.site](https://webhook.site), to get your webhook URL. For testing on your local machine, we recommend using [ngrok](https://ngrok.com/).

## Amazon S3

The files uploaded by the users can be pushed to the AWS S3 Bucket of your choice. You simply need to select the destination type as 'Amazon S3' and provide the AWS credentials, bucket/folder name, and access policy for storing the files.

{% hint style="info" %}
The data will be stored as S3 objects with the name **{{import\_id}}\_{{user\_id}}.csv** where **user\_id** is the custom user attribute that you reference via the **`setUser`**&#x6D;ethod while installing the importer code. The other 4 custom user attributes will be saved as the [user-defined metadata](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html) of the S3 object.
{% endhint %}

The AWS credentials need the following 3 minimum policies for uploading files to S3:

1. *ListBucket* policy is required for testing the connection.
2. *PutObject* is required to add objects to S3.
3. *PutObjectTagging* is required to add the tags (metadata) to the uploaded objects.

#### S3 Data Type

You have the option to store the data in S3 in either CSV or JSON format.&#x20;

## MySQL

Import CSV files and Excel sheets directly into your MySQL tables. How it works:

* Select the destination type as 'MySQL Database'.
* Connect your MySQL database by providing the credentials.
* Specify table name where you want the data to be pushed.

<div align="left"><img src="/files/-Mg5w072gaNw3hQyL6hK" alt="csv to mysql"></div>

* Map sheet columns to the table column.
* You can also map custom attributes to table columns.

<div align="left"><img src="/files/-Mg5xkDVYbD-Beu6RHOH" alt="map sheet to table columns"></div>

The user CSV data will then be directly be appended to the MySQL table.

## SQL Server

Import CSV files and Excel sheets directly into your SQL Server tables. How it works:

* Select the destination type as 'SQL Server Database'.
* Connect your SQL Server database by providing the credentials.
* Specify the table name where you want the data to be pushed.
* Click the 'Test Connection' button.
* If the connection is successful, then click the 'Map Columns' button and match the sheet template columns to the SQL Server table columns.
* You can also map custom attributes to table columns.
* Select between the following 2 operations:
  * **Insert** - The importer will always push the incoming CSV data as new records in the database.
  * **Upsert** - The importer will check if the record exists in the database. If the record exists, then it will be updated with the incoming data from the CSV. If the record does not exist, then a new record will be inserted. The record check will be done based on the index keys specified in the mapping modal.

<div align="center"><img src="/files/1s0y2wLCgswfhy6Z7xtU" alt="Define Unique Key for Upsert Operation"></div>

{% hint style="warning" %}
The **Upsert** operation will be significantly slower than the **Insert** operation. For the **Insert** operation, the records can be pushed in chunks. Whereas for the **Upsert** operation only one record can be processed at a time, and it requires multiple queries.
{% endhint %}

## Google Sheets

Import CSV files and Excel sheets directly into [Google Sheets](https://www.google.co.in/sheets/about/). Here is how it works:

* Select the destination type as 'Google Sheets'.
* Connect your Google account by clicking the Google button and accepting the relevant permissions.

{% hint style="info" %}
The importer requires permission to view the list of Google sheets in your account and edit sheet data.
{% endhint %}

* Provide the Google sheet name.
* Specify the worksheet name where you want the data to be pushed.
* Map the template columns to the Google sheet columns.
* You can also map custom attributes to sheet columns.

The user CSV data will then be directly be added to the Google sheet.

## Bubble

Import user CSV files and Excel sheets directly into your Bubble app. More information [here](/destinations/bubble.io).

## Notion

Import user CSV files and Excel sheets directly into your Notion databases. More information [here](/destinations/notion).

## PostgreSQL

Import CSV files and Excel sheets directly into your PostgreSQL tables. How it works:

* Select the destination type as 'PostgreSQL'.
* Connect your PostgreSQL database by providing the credentials.
* Specify the table name where you want the data to be pushed.

<div align="left"><img src="/files/1b73r3WN3NOMXINEiIbh" alt="PostgreSQL Data Destination Settings"></div>

* Map sheet columns to the table column.
* You can also map custom attributes to table columns.

<div align="left"><img src="/files/-Mg5xkDVYbD-Beu6RHOH" alt="map sheet to table columns"></div>

The user CSV data will then be directly be appended to the PostgreSQL table.

## Airtable

Import CSV files and Excel sheets directly into your [Airtable](https://airtable.com/). Here is how it works:

* Select the destination type as 'Airtable'.
* Connect your Airtable by providing the credentials.

{% hint style="info" %}
Steps to get the Airtable API Key are mentioned [here](https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key-).

Steps to get the Base ID are mentioned [here](https://support.airtable.com/docs/finding-airtable-ids#finding-ids-in-airtable-api).
{% endhint %}

* Specify the table name where you want the data to be pushed.
* Map sheet columns to the Airtable table column.
* You can also map custom attributes to table columns.

The user CSV data will then be directly appended to the Airtable table.

{% hint style="warning" %}
The data fields from the Airtable will be available in the Map Column modal only if they have **data in the first row**. You may add dummy data for each data field in the first row in order for them to appear in the Map Column modal.
{% endhint %}

There are 2 operations available for Airtable:

#### 1. Insert

Creates a new row in the table.

#### 2. Upsert

Update an existing row if a specified value already exists in a table, and insert a new row if the specified value doesn't already exist.

The column to check for uniqueness needs to be selected in the Column Mapping popup.

![](/files/c11Z4mBiCdRJEM14j3CT)

* If zero matches are found, a new row will be created.
* If one match is found, that row will be updated.
* **If multiple matches are found, the request will fail.**

## Zapier

Import user CSV files and Excel sheets to Zapier. More information [here](/destinations/zapier).

## Webflow <a href="#webflow" id="webflow"></a>

Import user CSV files and Excel sheets to Webflow. More information [here](/destinations/zapier).

## FTP Server

The files uploaded by the users can be pushed to your FTP Server. You simply need to select the destination type as 'FTP' and provide the conenction details and the folder name for storing the files.

{% hint style="info" %}
The data will be stored as CSV files with the name **{{import\_id}}\_{{user\_id}}.csv** where **user\_id** is the custom user attribute that you reference via the **`setUser`**&#x6D;ethod while installing the importer code.
{% endhint %}

## MongoDB

Easily import CSV files or Excel sheets into your MongoDB collections. Here's how:

1. **Choose MongoDB as the destination**\
   Start by selecting 'MongoDB' as your destination type.
2. **Connect your database**\
   Enter your MongoDB credentials to establish a secure connection.
3. **Set the target collection**\
   Specify the name of the collection where the data should be inserted.
4. **Map columns to fields**\
   Match the columns in your sheet to the corresponding fields in your MongoDB collection.
5. **Define field data types**\
   Assign appropriate data types to each field to ensure accurate data import.
6. **Use custom attribute mapping (optional)**\
   You can also map custom attributes from the CSV to specific fields in your collection.

Once set up, all submitted CSV data will be automatically inserted into your MongoDB collection.


# Bubble.io

Import customer CSV data to your Bubble app database with the csvbox.io importer.

## Demo App

See how it works [here](https://csvbox-demo.bubbleapps.io/version-test).

## 1. Configuring Bubble App

{% hint style="info" %}
You need to be on a [paid Bubble application plan](https://bubble.io/pricing/compare) to be able to use the Bubble API that is required to push external CSV data into the Bubble data store.
{% endhint %}

#### Data Settings

Create or update a data type in your Bubble app where you want to push the CSV data. Ensure that the data type is 'Publicly visible'. Add custom fields to the data type as per your requirements.

![Bubble Data Type](/files/-MlF9_Ri0Pvs4OKw6-Vv)

**Important:** Manually add at least one object (row) to the data type.

![Add object](/files/-MlFAAduaDJVvkR_JjZ7)

#### API Settings

1. Go to Settings
2. Go to the API page
3. Activate 'Data API'
4. Activate API for the data type where you want to push the CSV data
5. Generate and save the **API Private Key**

![API Settings](/files/-MlFF_Dohzrp_qyu0lax)

## 2. Setting up csvbox.io

Log in to [csvbox.io](https://app.csvbox.io/login).

Add a sheet.

![Add a Sheet](/files/-Mj38Dz7cxytaL48TR3g)

Add columns to the sheet. The column names should match column/object names for your data type in Bubble. Make sure you pay attention to upper and lower case letters on Bubble and match them in csvbox.io.

![Add Columns](/files/-Mj38czlRHi7IyDXeoR3)

Under the "**Settings**" section, for the "**Send Data To**" setting select the "**Bubble.io**" option.&#x20;

![Bubble.io Data Destination Settings](/files/-MlF3743QOGj852qd1T5)

Fill in the following fields:

* **App Name** - This is the name of your Bubble.io app.
* **Custom Domain Name** - If you have attached a custom domain name to your Bubble app then you need to provide it here.
* **Environment** - Pick an environment between TEST/DEVELOPMENT and PRODUCTION/LIVE where you want the CSV data to be sent.
* **API Private Key** - It is the API token that you generated while configuring the [API settings ](https://help.csvbox.io/destinations/bubble.io#api-settings)in the Bubble app.
* **Data Type** - The data type name where you want to push the CSV data.

Click the "**Test Connection**" button. It should be successful if all fields are inputted correctly.

Click the "**Map Columns**" button. It will open a modal where you can map the sheet columns to the object fields.

![Map Sheet Columns to Bubble Data Fields](/files/-MlF6Yue4ibif9qR4M_Q)

{% hint style="danger" %}
The data fields from the Bubble database will be available in the Map Column modal only if they have data in the first row. You may add dummy data for each data field in the first row in order for them to appear in the Map Column modal.
{% endhint %}

Click the "**Save**" button.

Go to the "**Code**" section of the sheet and note down the **Sheet License Key**.

![Sheet License Key](/files/-Mj-BO9coVZhfayCkqQy)

## 3. Adding the csvbox to Bubble

Install the [csvbox.io](https://bubble.io/plugin/csv--excel-importer-%7C-receive-json-1628686647935x372170116910546940) plugin to your Bubble app.

Drag the CSVBox Button element on your web page.

Save the **Sheet License Key** value from the sheet "**Code**" page (that we saved above) into the "**Sheet License Key**" property of the CSVBox element.

![Bubble App CSVBox Button Element](/files/-Mj-DZ9qMgvDLOBsfWPN)

Enter the import button label under the '**button\_text**' property. Optionally you can add CSS classes in the "**button\_classes**" property to stylize the csvbox.io import button.

{% hint style="info" %}
You can add custom user attributes (such as user\_id, user name, company name, etc) as values to the custom\_attribute*XX* properties of the CSVBox Button element. More information on custom attributes is available [here](https://help.csvbox.io/getting-started#referencing-the-user).
{% endhint %}

The csvbox.io import button should be available on your app. Your users can click the button to upload CSV files. You will get data in your Bubble Database.

The CSVBox Button element exposes two events that indicate the completion of the import process. The two events are:

1. **import\_success** - triggered when the CSV data gets imported successfully into your Bubble database.
2. **import\_fail** - triggered when the import failed completely or partially failed.

![CSVBox Button Element Events](/files/-Mj3BzfI7j1RPhqZt7TI)

You can add relevant actions to process the import result events.

## Why does my upload fail with unknown errors?

Bubble.io databases are very picky. The Column Names you are POSTing from the csvbox importer need to **EXACTLY** match what is in your Bubble.io database.

Check the following things:

* Make sure there are no extra columns in your csvbox.io sheet that do NOT map to a field/attribute in your Bubble.io database object. Bubble does not like extra columns that its database doesn't know about.
* The csvbox.io sheet column names should **EXACTLY** match the type names in your Bubble.io database object. Verify that capitalization and spaces are exactly the same as your Bubble.io data type object.
* In your Bubble.io app settings, click "API" and make sure you have all checkboxes checked for exposing the Data API and also checked for every database object you want to import for.

{% hint style="info" %}
The maximum number of items that can be created in Bubble.io via a single bulk request is currently 1000.&#x20;
{% endhint %}

## Adding Configuration Options

{% hint style="info" %}
coming soon
{% endhint %}


# OpenAPI

Send your CSV or Excel sheet data directly to any API described by an OpenAPI specification.

## How it works

1. **Select the destination type as 'OpenAPI'.** Open your sheet, go to **Destination**, and choose **OpenAPI** from the list.<br>

   <figure><img src="/files/ZdzXZqQV0pBQC7JeILoO" alt=""><figcaption></figcaption></figure>
2. **Load the OpenAPI specification.** Paste the OpenAPI Specification URL (a link to the API's spec file) and click Load Specificatio&#x6E;**.** This pulls in the list of available endpoints.
3. **Select the endpoint.** Pick the endpoint you want to send data to (for example, `POST /v1/prices - Create Tiered Price`).
4. **Set the HTTP method.** Choose the method for the request, such as **POST** to create new records.
5. **Choose the authentication type.** Select how the API verifies you (for example, **Bearer Token**) and enter your credentials in the field that appears (e.g. paste your **Bearer Token**).
6. **Test the connection.** Click Test Connection. A green checkmark means your settings are correct.
7. **Map sheet columns to the endpoint fields.** Match each column in your sheet to the matching field expected by the API. You can also map custom attributes.
8.

```
<figure><img src="/files/3o70B35bK848xD6XibHT" alt=""><figcaption></figcaption></figure>
```

9. Once set up, your sheet data will be sent to the API endpoint, one request per row.

## Example

This example shows how to test the **OpenAPI** destination end to end, for free, using a temporary inbox. You'll send sheet data to a test URL and watch it arrive live. Once it works, you simply swap in a real API.<br>

### What you'll use

* **webhook.site** — a free service that gives you a temporary URL and shows every request it receives. This stands in for a "real" API so you can see your data arriving.
* **GitHub Gist** — a free place to host your OpenAPI spec file so the destination can load it from a URL

### Step 1 - Get your test URL

Go to **webhook.site**. It instantly shows a unique URL at the top, like:

```
https://webhook.site/abc-123
```

Leave this tab open. Every request you send will appear here in real time.

### Step 2 - Create the spec file

Go to **gist.github.com** and create a new gist. Paste in the spec below, replacing the `servers` URL with **your** webhook.site URL from Step 1.

```json
{
  "openapi": "3.0.0",
  "info": { "title": "Test API", "version": "1.0" },
  "servers": [{ "url": "YOUR WEBHOOK URL" }],
  "paths": {
    "/": {
      "post": {
        "summary": "Send Row",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": { "type": "string" },
                  "email": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "OK" }
        }
      }
    }
  }
}
```

### Step 3 - Configure the destination

In your sheet, go to **Destination** and set it up:

| Field                     | Value                                                |
| ------------------------- | ---------------------------------------------------- |
| Send Data To              | **OpenAPI**                                          |
| OpenAPI Specification URL | your **Raw** gist URL → click **Load Specification** |
| Select Endpoint           | `POST / - Send Row`                                  |
| HTTP Method               | `POST`                                               |
| Authentication Type       | **None**                                             |

Then click **Test Connection**.

> **Why "None"?** webhook.site accepts anything, so there's no token to provide. Leaving authentication on "Bearer Token" with an empty token is the most common reason the test fails.

### Step 4 - Map your columns

Match each column in your sheet to a field in the API:

| Sheet column | API field |
| ------------ | --------- |
| Name         | name      |
| Email        | email     |

### Step 5 - Import and watch it work

Upload a small CSV with a couple of rows. As the import runs, switch to your webhook.site tab — each row appears as its own incoming POST request, with the data inside it.

That confirms the whole loop is working.


# Neon

Push your CSV files and Excel sheets directly into your Neon (serverless Postgres) database.

## How it works

1. **Select the destination type as 'Neon'.** Open your sheet, go to **Destination**, and choose **Neon** from the list.<br>

   <figure><img src="/files/S9kZOLQfOQcYDMDRrCJb" alt=""><figcaption></figcaption></figure>
2. **Connect your Neon database.** Enter your Neon credentials (enter the host, database name, user, and password). You can copy this from your Neon project dashboard under **Connection Details**.\
   &#x20;

   <figure><img src="/files/K4mbsozRiBTNZg36wDVy" alt=""><figcaption></figcaption></figure>
3. **Specify the table name.** Enter the name of the table where you want the data to be pushed.
4. **Map sheet columns to table columns.** Match each column in your sheet to the matching column in your Neon table. You can also map custom attributes to table columns.
5. Once set up, your CSV data will be appended directly to the Neon table.<br>


# Zapier

Push user CSV data to Zapier.

The data from files uploaded by the users can be pushed to Zapier as a trigger. This data can then be moved to any connected app of your choice.

Log in to [csvbox.io](https://app.csvbox.io/login).

Add a sheet.

<div align="left"><img src="/files/-Mj38Dz7cxytaL48TR3g" alt="Add a sheet"></div>

Add columns to the sheet as per your requirement.

<div align="left"><img src="/files/-Mj38czlRHi7IyDXeoR3" alt="Add columns"></div>

Under the "**Settings**" section, for the "**Send Data To**" setting select the "**Zapier**" option.

Click "**Connect Zap**".

<div align="left"><img src="/files/eJ8sYPvSzMMYxLTzaIwo" alt="Connect Zap"></div>

This will redirect you to Zapier.

Log in to your Zapier account.

Create a new Zap.

Select **csvbox.io** as the Trigger.

<div align="left"><img src="/files/8tb9MpxsCF40eWlGf0SC" alt="csvbox.io trigger"></div>

&#x20;Select '**New Import Row**' as the Trigger Event.

Connect your csvbox.io account by providing the API Key and API Secret Key. These keys can be found on the **Accounts** page of your csvbox dashboard.

![API crentials](/files/JICpNs41ft1lMkppRTMv)

Select the sheet (template) in the 'sheets' dropdown of the 'Set up Trigger' section. Data from this sheet will be pushed to Zapier one row at a time.

![Select Sheet](/files/bR9LpanRpbT4xCBvUmPI)

Then you can Test the trigger and continue with setting up the Action of your Zap.


# Notion

Import customer CSV data to your Notion database.

Steps to configure Notion data destination:

Log in to [csvbox.io](https://app.csvbox.io/login).

Add a sheet.

<img src="/files/G8iJvuOfIv8W6BAkOKiK" alt="" data-size="original">

Add columns to the sheet as per your requirement.

![](/files/OJoUu3jXAVHjcG2Yk1sH)

Under the "**Destination**" section, for the "**Send Data To**" setting select the "**Notion**" option.

Click the "**Connect Notion**" button.

![](/files/ma1h9wCeGZ0E1HSqaAOT)

This will redirect you to the Notion Integration Authorization screen.&#x20;

![](/files/2dIBgvHsKk4ykdtfKiV4)

You will have to select the page and the relevant workspace where you want the CSV data to be pushed.

![](/files/KTVzEmM7cwgqj5F4dVOt)

Click '**Allow access**'.

Login to your Notion account.

Go to your workspace where you want to receive the CSV data.

Click the "**Share"** button (top right of the dashboard.) Share the workspace with the '**CSV to Notion**' integration.

![Share workspace with csvbox integration](/files/a7VEfnVeJ4egI1GKO8v0)

{% hint style="info" %}
Only after sharing the workspace with the CSV to Notion integration, the Notion database will be visible in the Csvbox dashboard.
{% endhint %}

Go back to your Csvbox sheet and pick the Notion database from the list.

![](/files/znhLBHeNgPd7wkb15K3t)

Click the "**Map Columns**" button and map the sheet columns to the Notion database columns.

![](/files/L7vJoF8vTOWzViYsknsq)

Click the "**Save**" button.

The CSVs uploaded by the users will now be pushed to the Notion database.


# Webflow

Import customer CSV data to your Webflow CMS.

Steps to configure Webflow data destination:

Log in to [csvbox.io](https://app.csvbox.io/login).

Add a sheet.

<img src="/files/G8iJvuOfIv8W6BAkOKiK" alt="" data-size="original">

Add columns to the sheet as per your requirement.

![](/files/OJoUu3jXAVHjcG2Yk1sH)

Under the "**Destination**" section, for the "**Send Data To**" setting select the "**Webflow**" option.

Click the "**Connect Webflow**" button.

<div align="left"><img src="/files/HFyWfeeLOPYYIlBdVcvc" alt="Connect Webflow"></div>

This will redirect you to the Webflow CSVbox Integration Authorization screen.&#x20;

![Webflow Authorization](/files/utWbFYU7Hfah13BzhtPG)

You will have to select the site where you want the CSV data to be pushed.

Click '**Authorize Application**'.

Go back to your Csvbox sheet and pick the Webflow site and the collection from the list.

<div align="left"><img src="/files/Qt7vAS6vSvXkowKvyIDa" alt="Select site and collection"></div>

Click the "**Map Columns**" button and map the sheet columns to the Webflow CMS collection fields.

![Column Mapping](/files/3RIw7scAPeDF24Fa0WVY)

Click the "**Save**" button.

The CSVs uploaded by the users will now be pushed to the Webflow CMS.


# Softr

Collect spreadsheets from users and push clean data to Softr database.

CSVBox allows you to send imported spreadsheet data directly into **Softr Databases**, enabling no-code workflows such as internal tools, portals, directories, and dashboards powered by Softr.

With this destination, every successful CSV import will insert rows into a selected Softr table.

***

### When to use Softr as a Destination

Use the **Softr destination** when you want to:

* Import CSV / Excel data into Softr databases
* Power Softr apps with bulk uploads
* Allow non-technical users to upload structured data
* Replace manual Softr data entry with a guided importer

***

### Configure Softr Destination in CSVBox

Follow the steps below to configure Softr as your data destination.

#### Step 1: Select Softr as Destination

1. Go to **Sheet Settings → Destination**
2. Click **Select Destination**
3. Choose **Softr** from the list

***

#### Step 2: Enter Softr Credentials

Fill in the following required fields:

**API Key**

* Your Softr API Key
* Used to authenticate requests from CSVBox to Softr

**Database ID**

* The ID of the Softr database where data should be inserted
* This identifies the parent database inside Softr

**Table ID**

* The specific table inside the database that will receive the data
* Each imported row becomes a new record in this table

> 💡 How to get Database ID and Table ID from Softr
>
> 1. **log in to Softr**
> 2. Go to **Data → Databases**
> 3. Click on the **database** you want
> 4. Click on the **table** inside that database
> 5. Look at the **browser URL**
>
> You’ll see something like:
>
> <https://studio.softr.io/databases/><mark style="color:red;">**ecc84771-ca85-48f2-abc3**</mark>?table=<mark style="color:red;">**ajZqlE5O9KOy1**</mark><br>
>
> Extract the IDs:
>
> **Database ID:&#x20;**<mark style="color:red;">**ecc84771-ca85-48f2-abc3**</mark>
>
> **Table ID:&#x20;**<mark style="color:red;">**ajZqlE5O9KOy1**</mark>

***

#### Step 3: Test Connection

Click **Test Connection** to verify:

* API key validity
* Access to the specified database
* Access to the selected table

A successful test confirms CSVBox can write data to Softr.

***

#### Step 4: Map Columns

Click **Map Columns** to:

* Map CSV / Excel columns to Softr table fields
* Ensure field types are compatible
* Control which columns are sent to Softr

> Column mapping is mandatory before running an import.

***

#### Step 5: Save Configuration

Click **Save** to persist the Softr destination settings.

Your importer is now ready to send data directly into Softr.

***

### How Data Is Sent to Softr

* Each successfully imported row becomes **one record** in the selected Softr table
* Data is sent **after validation** and **after transforms**

***

### Troubleshooting

**Connection test fails**

* Verify API key permissions
* Double-check Database ID and Table ID
* Ensure the table exists and is accessible

**Columns not appearing**

* Revisit **Map Columns**
* Ensure Softr field names and types match incoming data

***

### Notes & Limitations

* Softr destination currently supports **row inserts**
* Large imports may take longer depending on Softr API limits


# Google BigQuery

Import CSV or Excel data directly into your Google BigQuery tables using CSVbox.

Follow the steps below to securely connect your BigQuery project and map your columns.

***

#### Step 1: Choose Google BigQuery as Destination

From your sheet’s **Destination Settings** page, select **Google BigQuery** from the “Send Data To” dropdown.

This enables all the necessary fields to configure your BigQuery connection.

***

#### Step 2: Enter Connection Details

You will need to fill in the following fields:

**Project ID**

Enter your Google Cloud **Project ID**.\
You can find this in your Google Cloud Console under **Home → Project Info → Project ID**.

**Dataset ID**

Specify the **Dataset ID** where your target table is located.\
This is the dataset name within your BigQuery project.

**Table Name**

Enter the exact **Table Name** where the data should be inserted.\
Make sure this table already exists in your BigQuery dataset.

***

#### Step 3: Upload JSON Key File

BigQuery requires authentication via a **Service Account Key**.

1. In your Google Cloud Console, go to\
   **IAM & Admin → Service Accounts → Keys → Add Key → Create New Key.**
2. Choose **JSON** as the key type and download the file.
3. In CSVbox, click **Browse**, select your JSON key file, and click **Upload**.

> ⚠️ Only `.json` files are accepted for this field.

This securely connects CSVbox to your BigQuery project.

***

#### Step 4: Test the Connection

After entering your credentials, click the **Test Connection** button.\
CSVbox will attempt to connect to your BigQuery project and validate the configuration.

* ✅ If successful, you’ll see a confirmation message.
* ❌ If there’s an error, double-check your credentials, dataset, or table name.

***

#### Step 5: Map Columns

Once your connection is validated, click **Map Columns** to proceed.\
You’ll be able to map the columns from your CSV file to the corresponding fields in your BigQuery table.

After mapping, all future imports through your CSVbox importer will automatically push data into your specified BigQuery table.

***

#### Notes

* Ensure that the BigQuery table schema matches the data types in your uploaded CSV to avoid type mismatch errors.
* CSVbox uses the **Service Account credentials** only to insert data — no other operations are performed.

***

#### Example Configuration

| Field         | Example Value               |
| ------------- | --------------------------- |
| Project ID    | `data-project-123`          |
| Dataset ID    | `sales_data`                |
| Table Name    | `orders`                    |
| JSON Key File | `bigquery-credentials.json` |

***

#### Troubleshooting

* **Invalid JSON key:** Ensure the key file is not modified and is a valid service account key.
* **Table not found:** Confirm that the specified table exists in the dataset.
* **Permission denied:** Verify that your service account has the `BigQuery Data Editor` role or higher.


# Supabase

Easily import CSV or Excel data directly into your Supabase tables.

### 🧭 Overview

The Supabase destination lets you push uploaded spreadsheet data from CSVBox straight into your Supabase database. It’s ideal for SaaS products that already use Supabase as their backend.

***

### ⚙️ Setup Instructions

#### 1. Choose Supabase as the Destination

In your **Sheet Settings**, set **Destination Type** to **Supabase**.

#### 2. Connect Your Database

Provide your Supabase project credentials:

* **Project URL** – found in your Supabase project settings
* **Service Role Key** – available under **Project → API → Service Role**

> 🔒 *CSVBox stores credentials securely and uses them only for insert operations.*

#### 3. Set the Target Table Name

Enter the **Table Name** where you want the imported data to be inserted. Ensure that this table already exists in your Supabase database. Click Test Connection to see if it is successful.

#### 4. Map Columns to Fields

Match each column in your CSV or Excel sheet to the corresponding field in your Supabase table.

#### 5. Use Custom Attribute Mapping *(Optional)*

You can also map custom attributes from the CSV to specific fields in your table.

***

### 🚀 Data Flow

Once the setup is complete, every submitted file will automatically insert the processed data into your specified Supabase table — row by row — without any manual intervention.

***

### 🧩 Notes

* Ensure your Supabase table schema matches the uploaded columns (case-sensitive).
* Insert operations use Supabase’s REST API under the hood.
* Bulk uploads are optimized for large CSVs.
* You can combine Supabase with CSVBox features like **validation rules**, **virtual columns**, and **AI Transforms** for advanced workflows.


# n8n

Push user spreadsheet data to n8n.

This allows you to directly connect your CSVbox imports to any of the apps supported by n8n without needing a webhook setup. With this integration, you can build workflows like:

* Save imported rows into a database (MySQL, Postgres, MongoDB, etc.)
* Send imported data to Slack, Teams, or Discord
* Trigger downstream API calls automatically
* Push leads into your CRM or marketing tools

***

### Step 1: Enable n8n as a Destination in CSVbox

1. Go to your [CSVbox Dashboard](https://dashboard.csvbox.io/).
2. Open the **Sheet Settings** for the sheet you want to integrate.
3. Navigate to the **Destinations** tab.
4. Click on **Add Destination** → select **n8n**.
5. Click on **Connect n8n** button. This will redirect you to n8n connections page.
6. Connect n8n with your csvbox.io account by providing the API Key and API Secret Key. These keys can be found on the **Accounts** page of your csvbox dashboard.
7. Save the destination.

***

### Step 2: Configure CSVbox Node in n8n

1. Open your **n8n Editor** (Cloud or Self-hosted).
2. Create a new workflow.
3. Add the **CSVbox node**.
4. Under **Credentials**, select CSVbox account.
5. Select the **Trigger** type:
   * **Import New Row** → Fires whenever new data is imported in CSVbox
6. Choose the **Sheet Name** from the dropdown list.

***

### Step 3: Add Processing Nodes

Now connect the CSVbox node to other nodes:

* **Database Nodes** → Store imported data in MySQL, Postgres, MongoDB
* **Messaging Nodes** → Send Slack or Teams alerts
* **Spreadsheet Nodes** → Save to Google Sheets, Airtable
* **HTTP Node** → Call custom APIs with imported data

***

### Example Workflow

A simple flow could be:

**CSVbox Node (trigger)** → **Google Sheets Node** → **Slack Notification Node**

This setup automatically saves imported data into a Google Sheet and then posts a confirmation message to Slack.

### Error Handling

* If the connected nodes succeed, the workflow continues normally.
* If there’s a failure (e.g. database timeout), n8n will show the error in **Executions → Error Log**.
* You can use n8n’s **Error Workflow** feature for retries or notifications.

***

### Alternative: Webhook Node

If you don’t want to use the official CSVbox node, you can still integrate using n8n’s **Webhook node**:

1. Add a **Webhook node** in n8n.
2. Copy the generated webhook URL.
3. Add this URL as a **Webhook destination** in CSVbox.
4. On each import, CSVbox will `POST` validated rows to this webhook.


# Pipedream

Import your CSV or Excel data directly into your Pipedream workflows with CSVBox.

This integration lets you trigger Pipedream automations in real time whenever a spreadsheet import is submitted — perfect for connecting data to your APIs, CRMs, or databases without writing custom code.

***

#### How It Works

CSVBox sends the imported CSV data to your selected Pipedream workflow as soon as a user submits it. Each import becomes a new event in Pipedream, where you can process or forward the data to hundreds of apps.

***

#### Example workflow:

1. User uploads CSV → CSVBox validates and sends data to Pipedream
2. Pipedream trigger receives the event
3. The workflow inserts rows into a database, sends Slack alerts, or posts data to your API

***

#### Setup Instructions

**Step 1: Choose Pipedream as your destination**

In your CSVBox **Sheet Settings**, select **Pipedream** as the destination and click **Save**.

**Step 2: Create a Pipedream trigger**

1. Log in to your [Pipedream account](https://pipedream.com/).
2. Create a **new workflow**.
3. When prompted to select a trigger source, choose **CSVBox > New Import**.

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

**Step 3: Connect CSVBox to Pipedream**

In the trigger setup screen, paste your **CSVBox API Key** and **API Secret Key**. You can find these under **CSVBox Dashboard → Account Settings → API Keys**.

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

**Step 4: Select your Sheet**

In the **Trigger Settings** window in Pipedream, choose the **Sheet Name** you want to connect from the dropdown.\
Click **Save and contine** to confirm.<br>

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

**Step 5: Verify the connection**

Return to **CSVBox → Sheet Settings → Destination**.\
The **Pipedream Connection** should now show as **successful**.<br>

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

**Step 6: Test the importer**

* Upload a sample CSV using your importer.
* Pipedream will instantly receive the data as an event payload.
* You can view the incoming event in your Pipedream workflow’s **Event** tab.

{% hint style="info" %}
After making changes in your Pipedream workflow (such as reconnecting CSVBox or editing the trigger), wait for **a few seconds** before testing from CSVBox. It can take a short time for Pipedream to **propagate configuration updates** across their servers.
{% endhint %}

***

#### Common Use Cases

* **Sync leads to your CRM** (HubSpot, Salesforce, Pipedrive, etc.)
* **Insert or update records** in databases (PostgreSQL, MySQL, Supabase, Airtable)
* **Send notifications** on Slack, Discord, or Email
* **Transform or enrich data** before passing it to another service

***


# Automation Platforms

Send CSVbox imports to Zapier, n8n, Make, Workato, or any workflow automation tool with one universal setup.

CSVbox makes it easy to connect your spreadsheet imports with any automation platform.<br>

Using the **`New Row Import`** trigger, you can send every imported row into your workflows — whether that means adding a contact to Salesforce, storing data in Postgres, or sending a Slack notification.

This guide shows you how to integrate CSVbox with popular tools like **Zapier, Make, n8n, Pipedream, IFTTT, Node-RED, Workato, Tray.io, Airflow, Prefect, Camunda**, and more.

Once connected, each new row uploaded by your users can automatically flow into your databases, CRMs, analytics pipelines, or enterprise systems — without writing custom code for every tool.

***

### 🔑 Core Pattern

1. **Webhook Event (Trigger)**
   * CSVbox sends a webhook on each row import.
   * Example payload:

     ```json
     [
       {
         "import_id": 79418895,
         "sheet_id": 55,
         "sheet_name": "Products",
         "row_number": 1,
         "total_rows": 1009,
         "env_name": "default", 
         "original_filename": "products01_24.csv",
         "row_data": {
               "Name": "TP-Link TL-WN822N Wireless N300 High Gain USB Adapter",
               "SKU": "AS-100221",
               "Price": "33.00",
               "Quantity": "3",
               "Image URL": "https://cdn.shopify.com/s/files/1/1491/9536/products/31jJOj1DS5L_070b4893-b7af-482f-8a15-d40f5e06760d.jpg?v=1521803806"
         },
         "custom_fields": {
           "user_id": "1002"
         }
       }  
     ]
     ```
2. **Automation Platform**
   * Receives this webhook.
   * Executes downstream actions: database insert, CRM update, Slack notification, etc.

***

### ⚙️ Integration Guides (Step by Step)

#### 🔹 1. Zapier

**Steps:**

1. Create a new Zap.
2. Select **Webhooks by Zapier** → *Catch Hook*.
3. Copy webhook URL.
4. In CSVbox Dashboard → Settings → Webhooks → paste the Zapier URL.
5. Perform a test import in CSVbox.
6. Confirm data mapping in Zapier.
7. Add actions (e.g., Google Sheets → Add Row, Salesforce → Create Contact).
8. Turn on Zap.

✅ Each new row import flows into Zapier automations.

***

#### 🔹 2. Make (Integromat)

**Steps:**

1. Create a new Scenario.
2. Add a **Webhook module** → Custom Webhook.
3. Copy webhook URL.
4. Paste it into CSVbox Dashboard → Webhooks.
5. Run scenario (listening mode).
6. Test import in CSVbox → payload appears.
7. Add modules (Sheets, MySQL, Slack, etc.).
8. Save & activate.

***

#### 🔹 3. Pipedream

**Steps:**

1. Create new Workflow.
2. Trigger: **HTTP/Webhook**.
3. Copy webhook URL to CSVbox Dashboard.
4. Test import in CSVbox.
5. Add Code Step:

   ```js
   export default defineComponent({
     props: { event: { type: "object" } },
     async run({ steps, $ }) {
       console.log("Row Imported:", this.event.data);
     }
   });
   ```
6. Add downstream integrations (DB, CRM, Slack).

***

#### 🔹 4. n8n

**Steps:**

1. Create workflow.
2. Add **Webhook node**.
3. Copy webhook URL to CSVbox Dashboard.
4. Test import → n8n captures payload.
5. Add downstream nodes (Postgres, Slack, HTTP).
6. (Optional) Use CSVbox Node to fetch rows by `import_id`.

***

#### 🔹 5. Node-RED

**Steps:**

1. Add **HTTP In node** → URL `/csvbox`.
2. Add **JSON node** → parse payload.
3. Add **Function node**:

   ```js
   msg.payload = msg.payload.data;
   return msg;
   ```
4. Connect target nodes (DB, Email, Slack).
5. Deploy and paste URL into CSVbox Dashboard.

***

#### 🔹 6. IFTTT

**Steps:**

1. In IFTTT, create an **Applet**.
2. “If This” → **Webhooks** → Receive a web request (`row_imported`).
3. Copy webhook key/URL.
4. In CSVbox Dashboard → paste URL.
5. “Then That” → pick action (e.g., send email, notification, IoT device).

✅ Lightweight consumer-friendly automations.

***

#### 🔹 7. Activepieces

**Steps:**

1. In Activepieces, create a new Flow.
2. Add **Webhook Trigger**.
3. Copy URL into CSVbox Dashboard.
4. Test import → Activepieces captures row data.
5. Add actions (e.g., Google Sheets, Airtable, APIs).
6. Save & activate.

***

#### 🔹 8. Automatisch

**Steps:**

1. Create a workflow.
2. Trigger → Webhook.
3. Copy webhook URL → paste into CSVbox Dashboard.
4. Import test row → Automatisch captures payload.
5. Add downstream integrations.

***

#### 🔹 9. Huginn

**Steps:**

1. Add **Webhook Agent**.
2. Copy Huginn webhook URL.
3. Paste into CSVbox Dashboard.
4. Test import → Huginn captures JSON.
5. Chain agents (Post Agent → Slack Agent → Email Agent).

***

#### 🔹 10. StackStorm

**Steps:**

1. Create **Webhook sensor** in StackStorm.
2. Rule example:

   ```yaml
   ---
   name: csvbox.new_row
   trigger: core.st2.webhook
   criteria:
     trigger.body.event: "row_imported"
   action:
     ref: core.local
     parameters:
       cmd: "echo {{trigger.body.data.email}} >> /var/log/csvbox.log"
   ```
3. Test import.

***

#### 🔹 11. Apache Airflow

**Steps:**

1. Setup Flask/Django endpoint to receive CSVbox webhook.
2. Configure CSVbox Dashboard → Webhooks → paste URL.
3. Trigger Airflow DAG with row data.
4. Example DAG:

   ```python
   from airflow import DAG
   from airflow.operators.python import PythonOperator

   def process_row(**context):
       data = context['dag_run'].conf
       print("Row imported:", data)

   dag = DAG("csvbox_import", start_date="2025-01-01")

   process = PythonOperator(
       task_id="process_row",
       python_callable=process_row,
       dag=dag
   )
   ```

***

#### 🔹 12. Prefect

**Steps:**

1. Setup Prefect Cloud/Server.
2. Create flow with `@flow` decorator.
3. CSVbox webhook triggers a Prefect API call.
4. Example:

   ```python
   from prefect import flow

   @flow
   def process_row(data: dict):
       print("Row Imported:", data)
   ```
5. Deploy and connect webhook.

***

#### 🔹 13. Kestra

**Steps:**

1. Create a Kestra flow with a **Webhook Trigger**.
2. Example:

   ```yaml
   id: csvbox_import
   namespace: csvbox
   tasks:
     - id: log
       type: io.kestra.core.tasks.debugs.Log
       message: "{{ trigger.body }}"
   ```
3. Paste Kestra webhook URL into CSVbox Dashboard.

***

#### 🔹 14. Temporal

**Steps:**

1. Define a workflow in Temporal.
2. Example:

   ```python
   @workflow.defn
   class ImportWorkflow:
       @workflow.run
       async def run(self, row: dict):
           print("Processing:", row)
   ```
3. CSVbox webhook → EventBridge/queue → starts Temporal workflow.

***

#### 🔹 15. Inngest

**Steps:**

1. In Inngest, define an event handler:

   ```js
   inngest.createFunction(
     { id: "csvbox-row-imported" },
     { event: "csvbox/row.imported" },
     async ({ event }) => {
       console.log("Row:", event.data)
     }
   )
   ```
2. Configure CSVbox webhook to send event payloads to Inngest.

***

#### 🔹 16. Windmill

**Steps:**

1. In Windmill, create a new flow.
2. Trigger → HTTP endpoint.
3. Paste URL into CSVbox Dashboard.
4. Test import → Windmill captures payload.
5. Add code step in Python/JS to process rows.

***

#### 🔹 17. Tray.io

**Steps:**

1. Create a Tray workflow.
2. Add a **Webhook Trigger**.
3. Copy URL into CSVbox Dashboard.
4. Import test row.
5. Add actions (Salesforce, NetSuite, DBs).

***

#### 🔹 18. Workato

**Steps:**

1. Create a Workato recipe.
2. Add **Webhook Trigger**.
3. Copy URL → paste into CSVbox Dashboard.
4. Test import → Workato captures row data.
5. Add enterprise actions (ERP, CRM, HR tools).

***

### 📦 Developer SDK (Optional)

Offer an SDK so dev teams can wire CSVbox directly:

```js
import { Csvbox } from "csvbox-sdk";

const client = new Csvbox({ apiKey: "xxx" });

client.on("row_imported", (row) => {
  console.log("Row Imported:", row);
  // push to CRM, DB, etc.
});
```

***

### ✅ Best Practices

* **Verify webhook signatures** (HMAC secret).
* **Retry failed actions** (add DLQ or retries).
* Store `import_id` + `row_id` for tracking.
* Log every payload for debugging.
* Secure API keys properly.

***

### 🚀 Example End-to-End Flow

**Use Case:**\
“When a customer uploads a new row with email + plan → push to CRM → notify sales on Slack.”

1. CSVbox → Webhook (`row_imported`)
2. n8n workflow → Insert into HubSpot CRM
3. Slack node → “🎉 New Pro plan signup: <john@example.com>”

***

👉 With this playbook, CSVbox integrates seamlessly with **every automation platform**:

* **Mainstream SaaS automation** → Zapier, Make, Pipedream, IFTTT
* **Open-source low-code** → n8n, Node-RED, Activepieces, Automatisch, Huginn
* **Enterprise iPaaS** → Workato, Tray.io
* **Data orchestration** → Airflow, Prefect, Kestra
* **Developer-first platforms** → Windmill, Temporal, Inngest, StackStorm
* **Business process automation** → Camunda


# Private Mode

User data does not transit CSVbox servers/networks and does not get stored in CSVbox data stores.

CSVbox offers a variety of configurations for handling user data.

<table><thead><tr><th width="149"> </th><th width="202">Standard</th><th width="178">No Store</th><th>Private</th></tr></thead><tbody><tr><td><strong>Data Processing</strong></td><td><ul><li>Client browser</li><li>CSVbox server</li></ul></td><td><ul><li>Client browser</li><li>CSVbox server</li></ul></td><td><ul><li>Client browser</li></ul></td></tr><tr><td><strong>Data Storage</strong></td><td><ul><li>CSVbox datastore</li></ul></td><td>-</td><td>-</td></tr></tbody></table>

### Standard Mode

<figure><img src="/files/ZRpkaRXhinpgzBYucGRb" alt=""><figcaption><p>Standard Mode</p></figcaption></figure>

This is the default configuration. User data is encrypted in transit and at rest. The data is pushed to your system and a copy of it is stored in the CSVbox database. It gets auto-deleted after one month. Your system can access the data from CSVbox datastore any time during this one month. Optionally the data is also available in the client browser.

### No Store Mode

<figure><img src="/files/KBUh2CsTXZUmRRgLJwTC" alt=""><figcaption><p>No Store Mode</p></figcaption></figure>

In this mode, the user data does not get stored in the CSVbox database. It is directly pushed to your system. Optionally the data is also available in the client browser.

{% hint style="warning" %}
Metadata that describes the import gets stored in CSVbox. This includes data such as Import ID, Import Start Time, End Time, Sheet ID, etc.
{% endhint %}

To activate this mode go to your CSVbox dashboard > Edit Sheet > **Security** tab > For **File Delete Policy** select **Do not store the file** option.

![](/files/t2SIxrJn5wSwoH9OQgX5)

### Private Mode

<figure><img src="/files/3Wy1Lw6kpXru83ANEe6v" alt=""><figcaption><p>Private Mode</p></figcaption></figure>

In the Private Mode, the user data does not transit CSVbox servers and is not stored in our databases. The data is processed in the client browser and available for consumption there. You can then choose to push the data to your system by implementing custom code.

{% hint style="warning" %}
Metadata that describes the import gets stored in CSVbox. This includes data such as Import ID, Import Start Time, End Time, Sheet ID, etc.
{% endhint %}

To activate Private Mode:

1. Go to your CSVbox dashboard > Edit Sheet > **Security** tab > For **File Delete Policy** select **Do not store the file** option.

![](/files/t2SIxrJn5wSwoH9OQgX5)

2. Go to Sheet **Settings** > **Destination** Tab > Select **--** for the **Send Data To** option.
3. Select **Yes** for the **Send a copy of data to client?** optio&#x6E;**.**

![](/files/ahBDO7Ww871JuF22Jjp9)

The user data will be available at the client in the format shown [**here**](/getting-started/3.-receive-data#data-at-the-client-side).


# Account

FAQs related to account information and subscription plans.

## Who processes csvbox.io orders?

Our order process is conducted by our online reseller [Paddle.com](https://paddle.com/). Paddle is the Merchant of Record for all our orders.

If you have any questions regarding order processing you can reach out to us or [Paddle](mailto:help@padde.com).

## Will my subscription automatically renew?

Yes, any subscription plan renews automatically. If you don’t want to be billed, cancel your subscription before the end of the expiration date.

## What will happen if I upgrade or downgrade to a different plan?

If you choose to upgrade to a more expensive plan, you will get a pro-rated discount according to the period you've already used on your existing plan.

If you choose to downgrade to a less expensive plan, the lower plan will be activated on the expiry date of your previous plan.&#x20;

## What will happen if I cancel my subscription?

If you cancel your subscription, it will remain active until its expiration date but won't be auto-renewed.

## Why has my subscription been canceled?

Your subscription can be canceled for one of the following reasons:

* If you cancel it yourself on the platform
* After too many payment declines, in which case our payment system will automatically cancel your subscription.

## What happens when I run out of the monthly quota of imports?

We will send you an email notification when your monthly usage hits 80% and 100%, so you have enough time to react and adapt your plan.

## Converting yearly subscription into a monthly subscription

You cannot convert your yearly subscription to monthly by yourself. Please [contact us](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur) for the same.

<br>


# Teams

Organize and control access to CSVbox by adding team members.

You can invite additional members of your team to your CSVbox dashboard via the [Team](https://app.csvbox.io/user/team) page.

When inviting new members of your team you’ll be asked what role they serve within your organization. This is because we limit dashboard functionality based on a user’s role. In the future, any user’s role can be changed by an admin in your team, at any time.

The roles and the dashboard areas they each have access (at a broad level) are as follows:

<table data-full-width="false"><thead><tr><th>Page</th><th align="center">Super Admin</th><th align="center">Admin</th><th align="center">Tech</th><th align="center">Finance</th></tr></thead><tbody><tr><td>Home</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td></tr><tr><td>Sheets</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center"></td></tr><tr><td>Imports</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center"></td></tr><tr><td>Plans</td><td align="center">✔</td><td align="center">✔</td><td align="center"></td><td align="center">✔</td></tr><tr><td>Settings</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center"></td></tr><tr><td>Teams</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td></tr></tbody></table>

Each account has one super admin that cannot be changed.

{% hint style="info" %}
The maximum number of team members that you can add is based on your [plan](https://csvbox.io/#pricing).
{% endhint %}


# Change Log

A record of all notable changes made to the application.

August 2026

* **Added:** CSVBox can now handle files with **up to 2M rows**, making it easier to import and process high-volume datasets without splitting them into smaller batches.
* **Added: Spreadsheet Review Table Enhancements** — Added column sorting (3-state cycle with numeric-aware ordering), advanced filtering (value/condition operators with multi-column logic), right-click context menus for cells/columns/rows, and full undo/redo support (up to 50 steps).
* **Added:** Sorting support in the upload table. Table rendering was reworked to handle sorted views correctly.
* **Added:** A new AI-powered feature that lets you generate a CSVBox sheet simply by describing your requirements.
* **Added:** Create, update, and manage Validation Functions, Virtual Columns, and Data Transforms via Sheet API

## July 2026

* **Added:** PDF metadata extraction - Automatically extract metadata fields from uploaded PDF documents.
* **Added:** Bulk AI Transform - Run AI-powered transformations across your data in bulk with a single click.
* **Updated:** Trim whitespace for List columns - Trim-whitespace support now works on list-type columns, including dynamic columns.
* **Updated:** Improved table & header detection - Enhanced OCR header matching, more reliable table merging, and improved text wrapping for wide columns.
* **Updated:** Cleaner upload view - Refreshed the upload modal UI with truncated long column names and tooltips for better readability.

## June 2026

* **Added:** [Neon](https://neon.tech) added as a data destination. Push imported data directly to Neon serverless Postgres databases, with built-in credentials management and connection handling.
* **Added:** [OpenAPI](/destinations/openapi) specification support as an upload destination type. Point CSVBox to an OpenAPI/Swagger spec URL and it will automatically route import data to your API endpoints.
* **Added:** Secret key + API key combination for Sheet API authorization, adding an extra layer of security for embedding and API access.
* **Improved:** REST API `auto_map` now matches columns using the same priority order as the frontend importer — display\_label first, then column\_name, then matching keywords.
* **Added:** CSVBox as an in-app integration in [Make](https://make.com).
* **Added:** CSVBox as an app in [Pabbly](https://www.pabbly.com).
* **Added:** CSVBox as an app in [IFTTT](https://ifttt.com).
* **Added:** [Xero](https://www.xero.com/) added as a data destination. Automatically push imported data to Xero with secure authentication and simplified connection setup.
* **New:** [Sheets API ](/advanced-installation/sheet-api)- You can now create and manage importer sheets programmatically instead of only through the dashboard.

## May 2026

* **New:** [AI Function Generator](/dashboard-settings/ai-function-generator) - Generate validation functions, virtual columns, data transforms, and regex patterns using plain-language prompts. Simply describe what you want, and CSVBox will generate compatible JavaScript or regex logic automatically.
* **New:** [Split Large CSV File Imports](/dashboard-settings/split-large-csv) - CSVBox can now automatically split large CSV files into smaller parts during import for improved reliability and browser performance. Multipart uploads reuse the same column mappings across all parts and are grouped together in the Imports dashboard for easier tracking.
* **New:** [AI Document Import](/dashboard-settings/ai-document-import) (PDFs, Images & DOCX) - You can now import data from PDFs, images, and documents using AI-powered table extraction.

## April 2026

* **Added** [Import Analytics](/dashboard-settings/import-analytics) **-** Get visibility into your import flow with built-in analytics. Track each step—from file upload and header selection to mapping, validation, and final submission. Identify drop-offs, measure completion rates, and uncover top errors so you can improve onboarding and reduce failed imports. Available for Pro plans and higher.
* **New:** [Server-Side Validation](/advanced-installation/server-side-validation) supports **column** error type.

## February 2026

* **Added sheet import & export** functionality. You can now export sheets from one CSVbox account and import them into another (or duplicate within the same account).
  * Securely copy sheets using a Sheet License Key and Export Key
  * Entire sheet configuration is duplicated automatically
  * Imported sheets receive a new unique license key
  * Perfect for staging → production workflows and template reuse
  * More information [here](/dashboard-settings/sheet-options-1#sheet-import-and-export)
* Added **Bulk Actions** to the Column Mapping screen, allowing users to reset all mappings or mark all columns as ignored in a single action.

{% columns %}
{% column %}

<figure><img src="/files/iexkpkFx2g96YREuHaEY" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column %}

<figure><img src="/files/ZJfR2qZiNLbolJepDUZS" alt=""><figcaption></figcaption></figure>
{% endcolumn %}
{% endcolumns %}

* Introduced **AI-powered auto column mapping** within Bulk Actions to automatically map file columns to template fields.

## January 2026

* **Added** custom styling Download and Upload functionality. You can now download your customized importer styling as a CSS file and upload it to another importer to instantly apply the same design. Perfect for maintaining consistent branding across multiple sheets—no re-styling required. More info [here](/dashboard-settings/styling#download-and-reuse-importer-styling-css).
* **Updated** the Import screen in the admin dashboard to display additional metadata for individual imports.

## December 2025

* **Added:** Support for AES-256-GCM and improved AES-CBC (V2) encryption options in Environment Variables for enhanced data security. More information [here](/advanced-installation/environment-variables#encrypting-environment-variables).
* **UI Enhancements:** Made several small UI tweaks to make the importer sleeker and more user-friendly. For example, the row filter dropdown (All / Error rows) is now a modern toggle switch, and the search box has been condensed into a search icon that expands on click.

  <div><figure><img src="/files/zfmxYKtdPaS41RJNBA2v" alt=""><figcaption><p>Row Filter</p></figcaption></figure> <figure><img src="/files/nk364GELXyqDy0H05TGa" alt=""><figcaption><p>Search Icon</p></figcaption></figure></div>
* **Added** [Pipedream ](/destinations/pipedream)as data destination.
* **Added** [n8n](/destinations/n8n) as data destination.
* Added [Sofr ](/destinations/softr)as data destination.
* **Improved** List Selection: We’ve added a new **“Replace all similar cells”** button for **List-type columns**. This allows you to replace **all cells with the same value as the currently selected cell** in one click. Perfect for quickly fixing repeated invalid values and speeding up data verification.

<div align="left"><figure><img src="/files/uxMQNNzeFSvEn6ok22PY" alt="" width="563"><figcaption></figcaption></figure></div>

* **Added** Quick Actions for Validation Errors: You can now take faster action on validation errors using the new **Quick Actions** button in the message bar. This provides one-click options to delete all invalid values, replace empty cells, or find and replace values—making it quicker to fix errors without navigating through individual cells.

<div align="left"><figure><img src="/files/y1fVKemRWpDcyFcN47Ix" alt="" width="563"><figcaption></figcaption></figure></div>

* **Added –** Newly supported languages include:
  * Bulgarian(`bg`)
  * Czech (`cs`)
  * Hungarian (`hu`)
  * Ukrainian(`uk`)

## November 2025

* **Added** [Supabase ](/destinations/supabase)as data destination.
* Newly supported languages include:
  * Chinese (Simplified) - zh-CN
  * Chinese (Traditional) - zh-TW
* **Added: Hide Cancel Button** option to hide the Cancel button located at the bottom-right corner of the importer. More information [here](/dashboard-settings/sheet-options-1#hide-cancel-button).
* **Added:** New client-side attributes (`ssv_enabled`, `ssv_fail`, `ssv_row_fail`, `ssv_table_error`) are now included in the response object when Server-Side Validation is enabled. More information [here](/advanced-installation/server-side-validation#additional-attributes-in-the-client-data-object).
* **Added** [Google BigQuery](/destinations/google-bigquery) as data destination.
* **Added** [Execution Time](/advanced-installation/data-transforms#execution-time) option to Data Transforms. You can now choose to run each transform **before** or **after** the Data Validation stage.

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

## 09 October 2025

* Added a new feature allowing users to select accepted file types for uploads — choose between **CSV**, **XLS**, and **XLSX** formats in your sheet settings.<br>

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

## 25 September 2025

* **Added – Language Support**\
  We’ve expanded CSVBox’s multilingual capabilities! Newly supported languages include:
  * Hindi (`hi`)
  * Bahasa Malaysia (Malay) (`ms`)
  * Russian (`ru`)
  * Vietnamese (`vi`)
  * Korean (`ko`)
  * Bahasa Indonesia (`id`)
  * Egyptian Arabic (`ar-EG`)
  * Urdu (`ur`)

## 08 September 2025

* **New:** [Server-Side Validation](/advanced-installation/server-side-validation) supports **table** and **row** error types.
* **UX:** Table errors render in an alert banner above the grid; row errors appear as a badge on the row number with a pop-over on click.

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

* **API:** You can mix `table`, `row`, and `cell` errors in one response. `type` is optional and defaults to `cell`. **No breaking changes.**

## 28 August 2025

* Added option to set the default row on the header selection screen. You can now choose which row is pre-selected on the Header Selection step.&#x20;
* Also available as an installation parameter - [**`default_header_row`**](/getting-started/2.-install-code#default_header_row).\
  \
  ![](/files/mTTGwMKewphCpenBWAWc)

## 14 August 2025

* Added [**Test API**](/destinations#test-api) destination **—** sends spreadsheet data to a unique [`webhooks.csvbox.io`](https://webhooks.csvbox.io/) URL (testing-only).

## 17 July 2025

* Added support for custom error messages (via `error_message` validator) for [regex type dynamic columns](https://help.csvbox.io/advanced-installation/dynamic-columns#validator-options).
* You can now use the `excel_value` validator with number-type dynamic columns to control whether to import **raw** or **formatted** values from Excel.

## 16 July 2025

* **Environment Variable Support for APIs** – You can now use environment variables in:

  1. Dynamic List API
  2. Multi-select Dynamic List API
  3. Success Message API
  4. Failed Message API
  5. Custom headers for all the above

  This makes it easier to manage configuration across environments.\
  \
  ![](/files/SKz8ouu5yub8oMbgo8Ts)

## 08 July 2025

* The importer now supports **dark** and **custom dark** themes for a better user experience.
* Added a [`theme`](https://help.csvbox.io/getting-started/2.-install-code#theme) selection option in the installation code for applying the theme at runtime.

## 12 June 2025

* Added **Excel Value Formatting Options** – Choose how numeric values are imported:

  * **Formatted**: As shown in Excel (e.g., "$1,200.00", "15%").
  * **RAW**: Strips formatting for clean numbers (e.g., 1200, 0.15).

  Ideal for better control in calculations and validations. More info [here](https://help.csvbox.io/dashboard-settings/validations#values-for-excel-setting).

## 20 May 2025

* Added MongoDB as data destination.

## 29 April 2025

* Introduced **AI Bulk Transformations**, allowing users to apply AI-driven changes to uploaded data using natural language prompts — all directly in the importer. Supports multi-language input, column-level modifications, and ensures complete privacy with no external data sharing. More information [here](/dashboard-settings/ai-bulk-transforms).

## 17 April 2025

* Introduced a setting to choose the default table view on the Verify Data screen — users can now select between "Show all rows" and "Show invalid rows only."

<div align="left"><figure><img src="/files/CvtFN6Enzi19nTR3ziU4" alt="" width="375"><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="/files/eVHbBibIBcBwciYS6JCE" alt="" width="375"><figcaption></figcaption></figure></div>

* Added a configuration option to set the default number of rows displayed on the Verify Data screen.

<div align="left"><figure><img src="/files/cuioEfGo6tUu31jlJnyo" alt="" width="375"><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="/files/g01h8VK994kJZWAXVJCr" alt="" width="375"><figcaption></figcaption></figure></div>

## 14 April 2025

* **Introduced the `column.isUnmapped` property** in the [`csvbox.columns`](https://help.csvbox.io/pages/ESMomiLmwEsfQm19yjFL#csvbox.columns) object to identify Unmapped Columns. This is now available in Data Transforms and Validation Functions.
* Added support for Unmapped Columns in Server Side Validation, allowing validation errors to be shown for Unmapped Columns.

## 07 April 2025

* Added support for bulk adding columns using a sample CSV file. More info [here](/dashboard-settings/add-columns-via-csv).

## 26 March 2025

* **Introduced the `column.isDynamic` property** in the [`csvbox.columns`](https://help.csvbox.io/pages/ESMomiLmwEsfQm19yjFL#csvbox.columns) object to identify Dynamic Columns. This is now available in Data Transforms and Validation Functions.
* Introduced a new configuration option to restrict adding new rows on the Verify Data screen.

<div align="left"><figure><img src="/files/njFEqc8rF1RP3OgrsTPs" alt="" width="375"><figcaption><p>Adding rows option</p></figcaption></figure></div>

## 21 March 2025

* Added OpenAI to the list of [Sub Processors](/legal/gdpr/data-processing-addendum#sub-processors) in the [DPA](/legal/gdpr/data-processing-addendum).&#x20;
* Why is OpenAI added as a subprocessor?\
  CSVbox leverages OpenAI’s industry-leading AI capabilities to enhance functionality within the importer app. As we build new AI-powered features, OpenAI has been included as a subprocessor.&#x20;

## 20 March 2025

* Added support for file submissions without requiring predefined template columns. Check out [Zero Template Column](/dashboard-settings/sheet-options-1#zero-template-columns) functionality

## 17 March 2025

* Expanded styling options with customizable colors, fonts, and layout sizes, allowing for a more consistent and professional appearance.

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

## 12 March 2025

* Added [Mapping Choices](/dashboard-settings/sheet-options-1#mapping-choice) - option for customers to map file columns to template fields or template fields to file columns.

## 11 February 2025

* Support for [Environment Variables](/advanced-installation/environment-variables) is now available in [Validation Functions](https://help.csvbox.io/pages/bZxJrXMOQvNdpytgOKDb#csvbox.environment), [Data Transforms](https://help.csvbox.io/pages/ESMomiLmwEsfQm19yjFL#csvbox.environment), and [Virtual Columns](https://help.csvbox.io/pages/mzxsW9LJgRdCzoWFUeGY#csvbox.environment).

## 24 January 2025

* Relocated the[ File Delete Policy](/dashboard-settings/sheet-options-1#file-delete-policy) from account-level settings to sheet-level settings.

## 14 January 2025

* Introduced the [`csvbox.columns`](https://help.csvbox.io/pages/ESMomiLmwEsfQm19yjFL#csvbox.columns) object for use in Data Transforms and Validation Functions.

## 06 January 2025

* Added [**auto\_map** ](/advanced-installation/rest-file-api#request-body)attribute to the REST File API to activate automatic column mapping during file submission.

## 03 December 2024

* Optimized the code to improve import performance 2x.

## 14 November 2024

* Introduced Data Transforms, enabling a new method for bulk editing datasets before pushing them to your app. Learn more here: [Data Transforms Guide](https://help.csvbox.io/advanced-installation/data-transforms)

## 23 October 2024

* Refined the CSVbox admin panel UI for a more intuitive and streamlined experience.

## 16 October 2024

* Added the option to hide File Upload Box for cases where only Copy-Paste data option is mandatory.
* For List-type columns introduced the ability to accept **list values** interchangeably with **display labels**.

## 07 October 2024

* Introduced several UI improvements to the importer, including a confirmation prompt when closing the import process.
* Added toggle functionality to enable or disable Virtual Columns and Validation Functions.

## 19 September 2024

* Added the option to show [Unmapped Columns](/advanced-installation/unmapped-columns) on the Validation Screen.

<div align="left"><figure><img src="/files/Ht9b42wVcbHWzrNfMZZk" alt="" width="373"><figcaption><p>Show Unmapped Columns</p></figcaption></figure></div>

## 28 August 2024

* Updated the [REST File API](/advanced-installation/rest-file-api) to enable direct uploading of file contents. Check [**import.file**](/advanced-installation/rest-file-api#direct-file-upload-beta) body param.

## 19 August 2024

* Added support for the Slovak language.

## 06 August 2024

* Added the functionality to make columns [**Read Only**](/dashboard-settings/sheet-options#read-only).

## 01 August 2024

* Enable end users to input a file/import description before uploading the file. More info [here](/dashboard-settings/sheet-options-1#import-description).

## 26 July 2024

* Added support for the Turkish language.

## 02 July 2024

* **Importer Enhancement:** The importer now handles password-protected files more effectively. Users will receive a message indicating that password-protected files cannot be read.

## 24 June 2024

* You can now provide a URL to redirect the page after the import is completed successfully.

<div align="left"><figure><img src="/files/FtsPR0GopcgvDcQoiSnk" alt="" width="368"><figcaption><p>Redirect URL</p></figcaption></figure></div>

## 18 June 2024

* &#x20;Two-Factor Authentication (2FA) is now available for all CSVbox users. This new feature is a part of our ongoing efforts to enhance security when accessing our platform. To activate 2FA, login to your CSVbox account > go to Accounts page > Click on 'Enable 2-factor Authentication' button and follow the instructions.

## 14 June 2024

* Added [**upload\_file\_url** ](/getting-started/2.-install-code#upload_file_url)and [**upload\_file\_worksheet\_name** ](/getting-started/2.-install-code#upload_file_worksheet_name)options to pre-load the importer with data from a file.

## 10 June 2024

* You can now encrypt environment variables using the [AES Everywhere library ](https://github.com/mervick/aes-everywhere)to protect sensitive data. More info [here](/advanced-installation/environment-variables#encrypting-environment-variables).

## 06 June 2024

* Updated the List Type Column validation to include the 'Accept Other Values' option.

<div align="left"><figure><img src="/files/Ep1pgi3WXf14qCjk0BHp" alt="" width="302"><figcaption><p>List Column Validation</p></figcaption></figure></div>

Selecting the 'Accept Other Values' option will allow the users to input values that are not found in the predefined list of acceptable values.

This option is also available for the Dynamic List, Multi-Select List and Dynamic Multi-Select List column types as well.

## 27 May 2024

* Added [**min\_rows** ](/getting-started/2.-install-code#min_rows)config option to enforce a minimum number of rows uploaded in a single sheet.
* Added option to configure a default delimiter for manual data entry.

<div align="left"><figure><img src="/files/p7hL9BSP67Ks0h5hvndW" alt="" width="361"><figcaption><p>Default delimiter</p></figcaption></figure></div>

## 20 May 2024

* You can now add[ **Environment Variables**](/advanced-installation/environment-variables) to define different environments (such as 'production', 'staging', 'local') and pass dynamic values to the importer.

## 28 April 2024

* You now have the capability to directly send JSON formatted files to S3, in addition to the CSV format files that were already sendable.

<div align="left"><figure><img src="/files/XgAxJUaPgErilnJyRNhA" alt="" width="351"><figcaption></figcaption></figure></div>

## 01 April 2024

* Added search functionality to quickly find the text in the uploaded sheet. It can be turned off via the admin dashboard.

<div align="left"><figure><img src="/files/2XwXiymtPSNjRYiX56Sz" alt="" width="375"><figcaption><p>Text Search</p></figcaption></figure></div>

## 19 March 2024

* Added functionality to find and replace text in the uploaded data.

<div align="left"><figure><img src="/files/7dHmPHh9LsHRJi1MBpcQ" alt="" width="375"><figcaption><p>Find and replace button</p></figcaption></figure></div>

<div align="left"><figure><img src="/files/hk0868YV1dJ0xfvKSSkE" alt="" width="320"><figcaption><p>Find and Replace Popup</p></figcaption></figure></div>

## 13 March 2024

* A new option was added to allow the default selection of columns as [Ignored Columns](/advanced-installation/ignored-columns).

<div align="left" data-full-width="false"><figure><img src="/files/NHIGmfY8bxHs3Nc7t7jj" alt="" width="363"><figcaption></figcaption></figure></div>

## 4 March 2024

* Added Azure Blob Storage as data destination.

## 21 Feb 2024

* Added Google Sheets to the[ target\_file\_name](/getting-started/2.-install-code#target_file_name) option. You can now provide custom file names for each new import in Google Sheets.
* New option to append [custom user attributes](/getting-started/2.-install-code#referencing-the-user) to [Dynamic List API](/dashboard-settings/validations#dynamic-list). This helps to identify the user in your app and generate custom list options.

<div align="left"><figure><img src="/files/sOxIhMawS1xpewpWvhqh" alt="" width="311"><figcaption><p>Custom User Attributes</p></figcaption></figure></div>

## 12 Feb 2024

* Added an option to skip the Data Validation screen if no errors are found.

<div align="left"><figure><img src="/files/sfvxrBZaof9OTcABnhV4" alt="" width="375"><figcaption></figcaption></figure></div>

## 02 Feb 2024

* The $9 Personal Plan has been discontinued. Existing subscribers to this plan will remain on their current subscription.

## 18 Jan 2024

* Added a new page for API keys. Each team will have one common set of API and Secret key. Only the Super Admin can generate/regenerate the keys. Admin and Tech roles can view the keys.
* Updated Airtable integration to move from the older API key based authentication to the newer Personal Access Token based authentication.

## 20 December 2023

* Added optimizations to improve speed.
* Fixed vulnerabilities as per the external pen testing report.

## 27 November 2023

* Improvements added to [Server Side Validation](/advanced-installation/server-side-validation) functionality. To allow the users to re-submit all the rows again (instead of error rows only) we have added the 'All Rows' option as shown below:

<div align="left"><figure><img src="/files/RXnmSsyFw3WrI2f32SDh" alt="" width="326"><figcaption></figcaption></figure></div>

## 06 November 2023

* Updated Teams feature to allow users to be part of multiple teams.

## 20 October 2023

* Added option to skip pushing of header row to the FTP data destination.

## 13 October 2023

{% hint style="success" %}
It is official! CSVbox is now SOC 2 Type 2 certified. [Read more](https://csvbox.io/soc-2-type-2).
{% endhint %}

## 05 October 2023

* Added the 'Allow Commas' option to the 'Number' type column.

<figure><img src="/files/aoI78E6hG1fwm7DYJugK" alt=""><figcaption><p>Allow Commas</p></figcaption></figure>

## 25 September 2023

* Add an option 'No headers in the sheet' on the Header Row selection page.

<figure><img src="/files/1SpKZn3JOwHJDDiH5D5I" alt=""><figcaption><p>No headers option</p></figcaption></figure>

## 22 September 2023

* Added [**target\_file\_name** ](/getting-started/2.-install-code#target_file_name)option to control the name of the file that gets pushed to the end destination.

## 20 September 2023

* Added dynamic column support to Validation Functions.

## 12 September 2023

* Added [Validation Functions](/advanced-installation/validation-functions). Code your custom validation logic in Javascript.

## 11 September 2023

* Added support for [worksheet selection](/dashboard-settings/sheet-options-1#worksheet-selection) in Excel files.

## 06 September 2023

* Deployed the functionality to add multiple [team members](/account/teams) to the CSVbox account.

## 29 August 2023

* Added new options - **max\_rows\_allow\_submit** and **max\_rows\_custom\_message** for [**max\_rows** ](/getting-started/2.-install-code#max_rows)validation.

## 02 August 2023

* Added [Currency ](/dashboard-settings/validations#currency)Column Type validation.

## 12 July 2023

* Added  **raw\_columns** object to the Import [Complete Webhook](/getting-started/3.-receive-data#import-complete-webhook) and the [Data at Client](/getting-started/3.-receive-data#data-on-the-client-side). This object contains all the column headers found in the raw spreadsheet file uploaded by the user.

## 07 July 2023

* Added [sample\_template\_url ](/getting-started/2.-install-code#sample_template_url)and [sample\_template\_button\_text ](/getting-started/2.-install-code#sample_template_button_text)configuration options. With these, you can now configure a dynamic sample template file for each user.

## 27 June 2023

* Added [Ignored columns](/advanced-installation/ignored-columns) - allow users to skip columns for data submission.

## 16 June 2023

* Infrastructure changes
* Bug fixes

## 15 May 2023

* Added functionality to hide copy-paste data option.
* Added option to skip confirmation message when accepting invalid data.
* UI enhancements to improve the speed of the admin dashboard.
* Enabled multiple importers on the same page for the Bubble plugin.
* Performance improvements across all destinations.

## 12 April 2023

* Few UI enhancements. Now the entire row gets highlighted when there is a validation issue in any one cell.

## 06 April 2023

* Added [Private Mode](/destinations/private-mode) for data processing.

## 27 Mar 2023

* Added [Server Side Validation](/advanced-installation/server-side-validation) (Beta) feature

## 09 Mar 2023

* Added the Danish language to the importer.

## 06 Mar 2023

* Added option to select between Sequential and Parallel sending of data via webhooks. More info [here](https://help.csvbox.io/destinations#request-type).

## 27 Feb 2023

* Option to add a new sheet in Google Sheets for each file upload.

![](/files/nDyo7p9zOvF5fkoH9C0y)

## 17 Feb 2023

* Added the importer event [onLoadStart](/getting-started/2.-install-code#onloadstart). It gets triggered when the importer iFrame starts loading.

## 14 Feb 2023

* Added [Upsert ](https://help.csvbox.io/pages/-MYigTAaYmsasIXlEb9V#2.-upsert)operation for Airtable.

## 09 Feb 2023

* Option to disable [user keywords](/dashboard-settings/sheet-options#matching-keywords) based column mapping.

![](/files/1Tjn1CnTorspuqoYW9Hd)

## 06 Feb 2023

* Added [`position` ](/advanced-installation/dynamic-columns#column-position)parameter to control the display order of the dynamic columns.

## 24 Jan 2023

* Added [Lazy Load](https://help.csvbox.io/getting-started/2.-install-code#lazy-load) option for importer initialization.
* Added multiple-level List-Dependent List validations.
* Added info icons on the Column Mapping page.
* Updated email validation to accept special characters.
* Fixed bugs, improved notifications and made minor UI changes to the admin section.

## 04 Jan 2023

* Added the Japanese language to the importer.

## 29 Dec 2022

* Added option to skip Column Mapping screen if there is an exact match of columns.

![](/files/uHaNq6HHiTNFwLX55KiP)

## 26 Dec 2022

* Added support for delimiters such as "." and "|" for [Multi-select List](https://help.csvbox.io/dashboard-settings/validations#multi-select-list) and [Dynamic Multi-select List](https://help.csvbox.io/dashboard-settings/validations#dynamic-multi-select-list) column types.&#x20;

## 18 Dec 2022

* Added Slovenian language.
* Added the option to provide help text in multiple languages.

![](/files/rZBO1h3enCYlRrsxcGaN)

## 05 Dec 2022

* `csvbox.row["total_rows"]` and `csvbox.row["row_number"]` [data variables](https://help.csvbox.io/pages/mzxsW9LJgRdCzoWFUeGY#csvbox.row) added to Virtual Columns.
* [`csvbox.virtual`](https://help.csvbox.io/pages/mzxsW9LJgRdCzoWFUeGY#csvbox.virtual) object added to Virtual Columns.

## 29 Nov 2022

* Added [1-click Resubmit button](/dashboard-settings/sheet-options-1#1-click-to-resubmit-the-same-csv-file-again) to trigger a new import on the recently submitted file.

<figure><img src="/files/B0ZDifleXgsccnB98Zyb" alt=""><figcaption><p>Resubmit</p></figcaption></figure>

## 25 Nov 2022

* Added [Multi-select List](https://help.csvbox.io/dashboard-settings/validations#multi-select-list) and [Dynamic Multi-select List](https://help.csvbox.io/dashboard-settings/validations#dynamic-multi-select-list) column types.&#x20;

## 21 Nov 2022

* Added option to view and delete [User Keywords](https://help.csvbox.io/dashboard-settings/sheet-options#matching-keywords).

![](/files/X2lpqhiUCvp78N4RMMoR)

## 16 Nov 2022

* Added a Reset All Mappings button to make it easier for the users to reset and remap the columns. This button is optional and its visibility can be controlled via the Sheet settings page.

<div align="left"><figure><img src="/files/b60QdlT0SOstZvRuPrNW" alt=""><figcaption><p>Reset Mappings Button</p></figcaption></figure></div>

## 14 Nov 2022

* Added the Italian language option for the importer front end.
* Added option to Copy sheet settings to an existing sheet. The data destination config and license key remain the same in the target sheet.

## 29 Oct 2022

* Optimized SQL Server integration code to improve speed.
* Added **data\_location** initialization parameter for specifying data and server location.

## 13 Oct 2022

* Fixed UI buys related to **max\_rows** option.
* Upgraded infrastructure and added optimizations for improved speed for EU region.
* Improved performance for imports with many Virtual Columns.

## 30 Sep 2022

* Updated [DPA ](/legal/gdpr/data-processing-addendum)to add Europe Data Residency option.

## 22 Sep 2022

* Added the option to select Europe (Germany) location for data residency. More info [here](/dashboard-settings/sheet-options-1#server-and-data-location).

## 05 Sep 2022

* Added Hebrew language for importer frontend.

## 01 Sep 2022

* Added **Time** Column type validation.
* The 100 column sheet restriction removed.

## 30 Aug 2022

* Added [Virtual Columns](/advanced-installation/virtual-columns). Create new columns by applying custom data transformation logic.

## 26 Aug 2022

* Added the Romanian language option for the importer frontend

## 25 Aug 2022

* Added [Import Link disable](/advanced-installation/import-links#activating-import-links) option

## 24 August 2022

* Added option to control[ import dialog size](/dashboard-settings/sheet-options-1#importer-dialog-size)
* Added decimal / integer validation

## 23 August 2022

* Updated React, Angular and Vuejs libraries for [onSubmit Importer event](/getting-started/2.-install-code#events)

## 16 August 2022

* Added [onSubmit Importer event](/getting-started/2.-install-code#events)

## 10 August 2022

* You now have the option to specify a default filler value for the column in case the incoming data is blank.&#x20;

<div align="left"><img src="/files/WIgvD5uI0pRTMA6ocC4s" alt="Default Value"></div>

## 08 August 2022

* Added [domain authorization](/dashboard-settings/sheet-options-1#domain-authorization) option. You can provide a list of approved *domains/sub-domains* for embedding the importer. The embedded importer will work on the whitelisted domains only.

## 03 August 2022

* Added functionality to configure custom success or failure messages at import complete. More info [here](/dashboard-settings/sheet-options-1#import-complete-messages).

## 01 August 2022

* Added [dynamic column](/advanced-installation/dynamic-columns) support for [Depenedent List](/dashboard-settings/validations#dependent-list) and [Dependent Dynamic List](/dashboard-settings/validations#dependent-dynamic-list) columns.
* Added the Polish language option for the importer frontend.

## 29 July 2022

* Added the Thai language option for the importer frontend.

## 22 July 2022

* Added options for controlling the close of importer dialog after the import is complete. ![](/files/IG02a5KKyzsl3yh2jBV5)

## 20 July 2022

* Added [Dependent Dynamic List Columns](/dashboard-settings/validations#dependent-dynamic-list).

## 07 July 2022

* Added **column\_mappings** object to the [import complete webhook](/getting-started/3.-receive-data#import-complete-webhook) and the [callback function](/getting-started/2.-install-code#callback-function). The **column\_mappings** object contains the user-defined mappings between the columns of the sheet (template) and columns in the uploaded CSV file.
* Added the **Import ID** column in the table on the Imports page of the CSVbox dashboard.
* The dynamic columns now support multiple date validation.

## 01 July 2022

* Added an **Export** button that downloads the validation errors and row data in an Excel sheet. More info [here](/dashboard-settings/sheet-options-1#export-button).
* Updated importer UI to show the truncated row count.

## 30 June 2022

* Added Max Rows option to the importer dashboard. It limits the number of rows per import.

<div align="left"><img src="/files/xovgf6BNZZrTSZD9UpRH" alt=""></div>

## 29 June 2022

* Added phone number validation based on the [libphonenumber.js library](https://catamphetamine.gitlab.io/libphonenumber-js/).
* Minor UI changes to the import modal. Changes include:
  * The importer modal is now full screen across all devices.
  * The column mapping table & validation error text are center-aligned for better visibility.

## 27 June 2022

* New styling options added - upload logo and custom fonts.

## 24 June 2022

* Added the Arabic language (ltr) support for the importer frontend.

## 23 June 2022

* Added option to allow displaying the import fail error messages to the end-user.

## 22 June 2022

* Added **Primary Color** custom theme option. You can stylize the importer with the primary color of your brand. More styling options coming soon.

## 10 June 2022

* The importer will attach the [custom user attributes](/getting-started/2.-install-code#referencing-the-user) as query parameters to the [Dynamic List](/dashboard-settings/validations#dynamic-list) API request. **csvbox\_** prefix will be added to the custom user attribute query parameters. This will help you identify the users/environment and return back a relevant list of values.

## 30 May 2022

* Added **Upsert** operation to MySQL, PostgreSQL and MS SQL data destinations. Instead of directly inserting the file data to the databases, the importer will first check if the record exists. If the record exists, then the row will be updated. Only if the record does not exist then a new row will be added.

## 23 May 2022

* Added [Dynamic List](/dashboard-settings/validations#dynamic-list) column type to accept valid list values via API, real-time.

## 20 May 2022

* New display options for the 'Select Header Row' page of the importer.

![DIsplay options](/files/oJhKVyHNEIoHBeBy4a1s)

1. **Skip this step?** - Hide/Show the 'Select header row' page to the users.
2. **Show encoding** - Hide/Show the character set selection list to the users.
3. **Switch Row/Columns** - Hide/Show the option to switch rows and columns to the users.

These settings help to remove the non-essential options from the importer and make the user experience cleaner.

## 19 May 2022

* Added the option (DELETE button on Accounts page) to delete and close down the CSVbox account permanently.

## 12 May 2022

* Added 'Multiple' option to Date Type columns. With this, you can provide multiple date formats that are allowed for the incoming data.

<div align="left"><img src="/files/4WPcR6mp95HFpBX2sBdM" alt=""></div>

## 11 May 2022

* Added the option to receive the CSV data in JSON format in the callback function. More info [here](/getting-started/3.-receive-data#data-at-the-client-side).

## 04 May 2022

* Added [Notion Data Destination](/destinations/notion)
* Fixed a bug for very large file uploads with [REST FileAPI](/advanced-installation/rest-file-api)

## 02 May 2022

### Added

* [SQL Server Data Destination](/destinations#sql-server)

## 26 April 2022

### Added

* [**allow\_invalid** option](https://help.csvbox.io/getting-started/2.-install-code#allow_invalid)

![allow\_invalid option](/files/YjtRxGU4uW4JGcoWCAar)

## 22 April 2022

### Added

* Ability to add help texts for each page (header selection, column mapping, data validation) on the importer

![Help text on Select Header page](/files/JnjaGR5fpRhs9dKpqPIA)

## 13 April 2022

### Added

* Yearly pricing plans
* [REST File API](/advanced-installation/rest-file-api) beta
* New [importer events](/getting-started/2.-install-code#events) (onReady, onClose)
* [request\_headers](/getting-started/2.-install-code#request_headers) option to send headers at run-time
* PATCH request type
* **total\_rows** parameter to the [API output](/getting-started/3.-receive-data#sample-response).
* **original\_filename** parameter to the [import complete webhook](/getting-started/3.-receive-data#import-complete-webhook) and importer [callback function](/getting-started/2.-install-code#callback-function).

### Updated

* Fixed issue related to freezing of the importer
* Updated the code from Laravel 8 to Laravel 9
* Added new layers of database security
* Fixed issue related to the downloading of the template files
* Added custom attribute mapping for Airtable&#x20;
* UI enhancements

## 07 March 2022

### Added

* Custom message for regex validation.

<div align="left"><img src="/files/RQZ2GmfaxAIk7mPXcYH4" alt=""></div>

## 01 March 2022

### Added

* Portuguese language support for the importer frontend.
* [**language** sheet option](https://help.csvbox.io/getting-started#additional-options) to select the frontend language while initializing the importer.

## 23 February 2022

### Updated

* You can now view the translated texts for all supported languages and submit suggestions for changes.

![Language Correction](/files/vq4zvO60YuvQHZAwpHZ0)

## 22 February 2022

### Added

* Dutch language support for the importer frontend.

## 11 February 2022

### Added

* [Unmapped Columns](/advanced-installation/unmapped-columns): Allowing users to submit columns not included in the sheet template.

## 08 February 2022

### Added

* [FTP Data Destination](/destinations#ftp-server)

## 07 February 2022

### Added

* Header row selection step in the importer.

![](/files/2obIry4o28DyALQDXfL8)

## 03 February 2022

### Added

* Importer internationalization. The importer now supports German, French and Spanish languages.

## 17 January 2022

### Added

* [max\_rows](https://help.csvbox.io/getting-started#additional-options) config option to restrict the number of rows uploaded in a single sheet.

## 10 January 2022

### Added

* [Zapier Data Destination](/destinations/zapier)

## 4 January 2022

### Updated

* Included 'Custom' validation option for Date Type columns. Date formatting options available [here](https://help.csvbox.io/validations#date).

<div align="left"><img src="/files/7VNMHQiSxnit8Wu4d1hS" alt=""></div>


# Privacy Policy

Revision Date: 13 April 2021

This policy details how data about you is used when you access our websites and services, or interact with us. If we update it, we will revise the date mentioned above.

## **Protecting your privacy**

* We take precautions to prevent unauthorized access to or misuse of data about you.
* We do not run ads, other than the classifieds posted by our users.
* We do not share your data with third parties for marketing purposes.
* We do not send you unsolicited communications for marketing purposes.
* We do provide email proxy & relay services to reduce unwanted email.
* Please review privacy policies of any third party sites linked to from csvbox.io
* We do not respond to "Do Not Track" signals (see allaboutdnt.com).

## **Data we collect, use and disclose:**

Below is a list of all the types of data we collect, where we got it, why we collected it and the categories of third parties to whom we disclosed it. We do not sell your data to third parties.

| **Data type**                                                                                           | **Where we got it**                                  | **Why collected**                                                               | **Disclosed to**                                  |
| ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------- |
| First and last name                                                                                     | User entry                                           | Facilitating transactions and personalizing your use of csvbox.io               | No one                                            |
| Email address                                                                                           | User entry                                           | Account creation and combatting fraud/abuse                                     | No one                                            |
| Geographic location (latitude and longitude)                                                            | User entry, IP/geolocation providers                 | Personalizing your use of csvbox.io and combatting fraud/abuse                  | Analytics & Customer Engagement service providers |
| HTTP browser cookie                                                                                     | User's browser, csvbox.io web server                 | Facilitating and personalizing your use of csvbox.io and combatting fraud/abuse | No one                                            |
| Information about your device and browser such as device ID, browser version, operating system, plugins | User's browser, mobile app                           | Facilitating and personalizing your use of csvbox.io and combatting fraud/abuse | Analytics & Customer Engagement service providers |
| IP address                                                                                              | User's browser, mobile app, IP/geolocation providers | Combatting fraud/abuse                                                          | Service providers that help us combat fraud/abuse |
| Web page views, access times, HTTP headers                                                              | User's browser, mobile app                           | Combatting fraud/abuse                                                          | Analytics & Customer Engagement service providers |
| Login credentials for services as AWS, Dropsbox, Google Sheets etc                                      | User entry                                           | To authenticate users for transferring data                                     | No one                                            |

We may share some or all of the above-listed data in the following circumstances:

* to respond to subpoenas, search warrants, court orders, or other legal process.
* to protect the rights, property, or safety of csvbox.io users, csvbox.io, or the general public.
* at your direction (e.g. if you authorize us to share data with other users).
* in connection with a merger, bankruptcy, or sale/transfer of assets.

## **Data we store**

* We retain data as needed to facilitate and personalize your use of csvbox.io, combat fraud/abuse and/or as required by law.
* We make good faith efforts to store data securely, but can make no guarantees.
* You may access and update certain data about you via your account login.

## **Your Rights**

**Right to know:** You have the right to request that we disclose the data we collect, use and disclose, and other information relating to data we collect about you.

**Right to delete:** You have the right to request the deletion of data that we have collected from you, subject to certain exceptions.

**Right to non-discrimination:** You have the right not to receive discriminatory treatment for exercising the rights listed above.

You may submit a request to know or delete [here](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur).

Only you, or someone you authorize to act on your behalf, may make a request to know or delete your data. An authorized agent may make a request on your behalf by providing written permission signed by you.

We will need to confirm your identity before processing your request by asking you to log into your existing account (if you are a registered user) or by asking you for additional information, such as a government issued ID, to confirm your identity against information we have already collected.

Please note that removal does not ensure complete or comprehensive removal of said content or information from the Internet.

## **International Users**

By accessing csvbox.io or providing us data, you agree we may use and disclose data we collect as described here or as communicated to you, transmit it outside your resident jurisdiction, and store it on servers in the United States.

## Data Policy for Uploaded Data

csvbox.io provides an import service allowing your users to upload data. The data policy [here ](https://help.csvbox.io/legal/data-policy)is applicable to the data uploaded by the users.

## **Contact**

If you have any questions or concerns about our privacy policy and practices please reach out [here](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur).


# Terms of Use

Last updated: 02 December 2025

## Introduction

Welcome to Thalia Technologies Private Limited ("**Company**", "**we**", "**us**", "**our**", "**csvbox.io**", "**app**")! As you have just clicked our Terms of Service, please pause, grab a cup of coffee and carefully read the following pages.

These Terms of Service (“Terms”, “Terms of Service”) govern your use of our web pages located at csvbox.io, app.csvbox.io, and other sub-domains of csvbox.io operated by Thalia Technologies.

Our Privacy Policy also governs your use of our Service and explains how we collect, safeguard and disclose information that results from your use of our web pages. Please read it here <https://help.csvbox.io/legal/privacy>.

Your agreement with us includes these Terms and our Privacy Policy (“Agreements”). You acknowledge that you have read and understood Agreements, and agree to be bound of them.

If you do not agree with (or cannot comply with) Agreements, then you may not use the Service. These Terms apply to all visitors, users and others who wish to access or use Service.

Thank you for being responsible.

## Communications

By creating an Account on our Service, you agree to subscribe to newsletters, marketing or promotional materials, and other information we may send. However, you may opt-out of receiving any, or all, of these communications from us by following the unsubscribe link.

## Contests, Sweepstakes, and Promotions

Any contests, sweepstakes or other promotions (collectively, “Promotions”) made available through Service may be governed by rules that are separate from these Terms of Service. If you participate in any Promotions, please review the applicable rules as well as our Privacy Policy. If the rules for a Promotion conflict with these Terms of Service, Promotion rules will apply.

## Subscriptions

Some parts of the Service are billed on a subscription basis (“Subscription(s)”). You will be billed in advance on a recurring and periodic basis (“Billing Cycle”). Billing cycles are set either on a monthly or annual basis, depending on the type of subscription plan you select when purchasing a Subscription.

At the end of each Billing Cycle, your Subscription will automatically renew under the exact same conditions unless you cancel it or we cancel it. You may cancel your Subscription renewal either through your online account management page or by contacting our customer support team.

A valid payment method, including credit card or PayPal, is required to process the payment for your subscription. You shall provide us with accurate and complete billing information including full name, address, state, zip code, telephone number, and valid payment method information. By submitting such payment information, you automatically authorize us to charge all Subscription fees incurred through your account to any such payment instruments.

Should automatic billing fail to occur for any reason, we will issue an electronic invoice indicating that you must proceed manually, within a certain deadline date, with the full payment corresponding to the billing period as indicated on the invoice.

## Fee Changes

Our company, in its sole discretion and at any time, may modify Subscription fees for the Subscriptions. Any Subscription fee change will become effective at the end of the then-current Billing Cycle.

We will provide you with reasonable prior notice of any change in Subscription fees to give you an opportunity to terminate your Subscription before such change becomes effective.

Your continued use of Service after the Subscription fee change comes into effect constitutes your agreement to pay the modified Subscription fee amount.

## Refunds

Our company uses Paddle as our Merchant of Record for all payments. All transactions are processed securely through Paddle in accordance with their Buyer Protection policies.

We offer refunds in line with Paddle’s refund guidelines. If you believe a charge was made in error, or if you are unsatisfied with your purchase, you may request a refund within the applicable time window set by Paddle. Refund eligibility may vary depending on the specific product, subscription term, and usage conditions.

Please note:

* Refunds are not guaranteed in all cases.
* Paddle may assess eligibility based on factors such as usage, time since purchase, or potential misuse.
* Approved refunds will be issued by Paddle to the original payment method.

## Content

Our Service allows you to post, link, store, share and otherwise make available certain information, text, graphics, videos, files, or other material (“Content”). You are responsible for Content that you post on or through Service, including its legality, reliability, and appropriateness.

By posting Content on or through Service, You represent and warrant that: (i) Content is yours (you own it) and/or you have the right to use it and the right to grant us the rights and license as provided in these Terms, and (ii) that the posting of your Content on or through Service does not violate the privacy rights, publicity rights, copyrights, contract rights or any other rights of any person or entity. We reserve the right to terminate the account of anyone found to be infringing on a copyright.

You retain any and all of your rights to any Content you submit, post, or display on or through Service and you are responsible for protecting those rights. We take no responsibility and assume no liability for Content you or any third party posts on or through Service.&#x20;

We have the right but not the obligation to monitor and edit all Content provided by users.

In addition, Content found on or through this Service are the property of Thalia Technologies or used with permission. You may not distribute, modify, transmit, reuse, download, repost, copy, or use said Content, whether in whole or in part, for commercial purposes or for personal gain, without express advance written permission from us.

## Prohibited Uses

You may use Service only for lawful purposes and in accordance with Terms. You agree not to use Service:

1. In any way that violates any applicable national or international law or regulation.
2. For the purpose of exploiting, harming, or attempting to exploit or harm minors in any way by exposing them to inappropriate content or otherwise.
3. To transmit, or procure the sending of, any advertising or promotional material, including any “junk mail”, “chain letter,” “spam,” or any other similar solicitation.
4. To impersonate or attempt to impersonate Company, a Company employee, another user, or any other person or entity.
5. In any way that infringes upon the rights of others, or in any way is illegal, threatening, fraudulent, or harmful, or in connection with any unlawful, illegal, fraudulent, or harmful purpose or activity.
6. To engage in any other conduct that restricts or inhibits anyone’s use or enjoyment of Service, or which, as determined by us, may harm or offend Company or users of Service or expose them to liability.

Additionally, you agree not to:

1. Use Service in any manner that could disable, overburden, damage, or impair Service or interfere with any other party’s use of Service, including their ability to engage in real-time activities through Service.
2. Use any robot, spider, or other automatic device, process, or means to access Service for any purpose, including monitoring or copying any of the material on Service.
3. Use any manual process to monitor or copy any of the material on Service or for any other unauthorized purpose without our prior written consent.
4. Use any device, software, or routine that interferes with the proper working of Service.
5. Introduce any viruses, trojan horses, worms, logic bombs, or other material which is malicious or technologically harmful.
6. Attempt to gain unauthorized access to, interfere with, damage, or disrupt any parts of Service, the server on which Service is stored, or any server, computer, or database connected to Service.
7. Attack Service via a denial-of-service attack or a distributed denial-of-service attack.
8. Take any action that may damage or falsify the Company rating.
9. Otherwise, attempt to interfere with the proper working of Service.

## Analytics

We may use third-party Service Providers to monitor and analyze the use of our Service such as:

### Google Analytics

Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network.

For more information on the privacy practices of Google, please visit the Google Privacy Terms web page: <https://policies.google.com/privacy?hl=en>

We also encourage you to review Google's policy for safeguarding your data: <https://support.google.com/analytics/answer/6004245>.

### Cloudflare analytics

Cloudflare analytics is a web analytics service operated by Cloudflare Inc. Read the Privacy Policy here: <https://www.cloudflare.com/privacypolicy/>&#x20;

## No Use By Minors

Service is intended only for access and use by individuals at least eighteen (18) years old. By accessing or using any of Company, you warrant and represent that you are at least eighteen (18) years of age and with the full authority, right, and capacity to enter into this agreement and abide by all of the terms and conditions of Terms. If you are not at least eighteen (18) years old, you are prohibited from both the access and usage of Service.

## Accounts

When you create an account with us, you guarantee that you are above the age of 18, and that the information you provide us is accurate, complete, and current at all times. Inaccurate, incomplete, or obsolete information may result in the immediate termination of your account on Service.

You are responsible for maintaining the confidentiality of your account and password, including but not limited to the restriction of access to your computer and/or account. You agree to accept responsibility for any and all activities or actions that occur under your account and/or password, whether your password is with our Service or a third-party service. You must notify us immediately upon becoming aware of any breach of security or unauthorized use of your account.

You may not use as a username the name of another person or entity or that is not lawfully available for use, a name or trademark that is subject to any rights of another person or entity other than you, without appropriate authorization. You may not use as a username any name that is offensive, vulgar, or obscene.

We reserve the right to refuse service, terminate accounts, remove or edit content, or cancel orders in our sole discretion.

## Intellectual Property

Service and its original content (excluding Content provided by users), features, and functionality are and will remain the exclusive property of Thalia Technologies and its licensors. Service is protected by copyright, trademark, and other laws of the United States and foreign countries. Our trademarks and trade dress may not be used in connection with any product or service without the prior written consent of Thalia Technologies.

## Error Reporting and Feedback

You may contact us with information and feedback concerning errors, suggestions for improvements, ideas, problems, complaints, and other matters related to our Service (“Feedback”). You acknowledge and agree that: (i) you shall not retain, acquire or assert any intellectual property right or other rights, title, or interest in or to the Feedback; (ii) Company may have developed ideas similar to the Feedback; (iii) Feedback does not contain confidential information or proprietary information from you or any third party, and (iv) Company is not under any obligation of confidentiality with respect to the Feedback. In the event the transfer of the ownership to the Feedback is not possible due to applicable mandatory laws, you grant Company and its affiliates an exclusive, transferable, irrevocable, free-of-charge, sub-licensable, unlimited, and perpetual right to use (including copy, modify, create derivative works, publish, distribute and commercialize) Feedback in any manner and for any purpose.

## Links To Other Web Sites

Our Service may contain links to third party web sites or services that are not owned or controlled by us.

We has no control over, and assume no responsibility for the content, privacy policies, or practices of any third-party websites or services. We do not warrant the offerings of any of these entities/individuals or their websites.

YOU ACKNOWLEDGE AND AGREE THAT THALIA TECHNOLOGIES SHALL NOT BE RESPONSIBLE OR LIABLE, DIRECTLY OR INDIRECTLY, FOR ANY DAMAGE OR LOSS CAUSED OR ALLEGED TO BE CAUSED BY OR IN CONNECTION WITH USE OF OR RELIANCE ON ANY SUCH CONTENT, GOODS OR SERVICES AVAILABLE ON OR THROUGH ANY SUCH THIRD-PARTY WEB SITES OR SERVICES.

WE STRONGLY ADVISE YOU TO READ THE TERMS OF SERVICE AND PRIVACY POLICIES OF ANY THIRD-PARTY WEBSITES OR SERVICES THAT YOU VISIT.

## Limitation Of Liability

EXCEPT AS PROHIBITED BY LAW, YOU WILL HOLD US AND OUR OFFICERS, DIRECTORS, EMPLOYEES, AND AGENTS HARMLESS FOR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGE, HOWEVER, IT ARISES (INCLUDING ATTORNEYS' FEES AND ALL RELATED COSTS AND EXPENSES OF LITIGATION AND ARBITRATION, OR AT TRIAL OR ON APPEAL, IF ANY, WHETHER OR NOT LITIGATION OR ARBITRATION IS INSTITUTED), WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, OR ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT, INCLUDING WITHOUT LIMITATION ANY CLAIM FOR PERSONAL INJURY OR PROPERTY DAMAGE, ARISING FROM THIS AGREEMENT AND ANY VIOLATION BY YOU OF ANY FEDERAL, STATE, OR LOCAL LAWS, STATUTES, RULES, OR REGULATIONS, EVEN IF COMPANY HAS BEEN PREVIOUSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. EXCEPT AS PROHIBITED BY LAW, IF THERE IS LIABILITY FOUND ON THE PART OF THE COMPANY, IT WILL BE LIMITED TO THE AMOUNT PAID FOR THE PRODUCTS AND/OR SERVICES, AND UNDER NO CIRCUMSTANCES WILL THERE BE CONSEQUENTIAL OR PUNITIVE DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF PUNITIVE, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, SO THE PRIOR LIMITATION OR EXCLUSION MAY NOT APPLY TO YOU.

## Termination

We may terminate or suspend your account and bar access to Service immediately, without prior notice or liability, under our sole discretion, for any reason whatsoever and without limitation, including but not limited to a breach of Terms.

If you wish to terminate your account, you may simply cancel the subscription from the app dashboard and discontinue using the Service.

All provisions of Terms which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity, and limitations of liability.

## Governing Law

These Terms shall be governed and construed in accordance with the laws of Mumbai, India without regard to its conflict of law provisions.

Our failure to enforce any right or provision of these Terms will not be considered a waiver of those rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us regarding our Service and supersede and replace any prior agreements we might have had between us regarding Service.

## Changes To Service

We reserve the right to withdraw or amend our Service, and any service or material we provide via Service, in our sole discretion without notice. We will not be liable if for any reason all or any part of Service is unavailable at any time or for any period. From time to time, we may restrict access to some parts of Service, or the entire Service, to users, including registered users.

## Amendments To Terms

We may amend Terms at any time by posting the amended terms on this site. It is your responsibility to review these Terms periodically.

Your continued use of the Platform following the posting of revised Terms means that you accept and agree to the changes. You are expected to check this page frequently so you are aware of any changes, as they are binding on you.

By continuing to access or use our Service after any revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, you are no longer authorized to use Service.

## Waiver And Severability

No waiver by Company of any term or condition set forth in Terms shall be deemed a further or continuing waiver of such term or condition or a waiver of any other term or condition, and any failure of Company to assert a right or provision under Terms shall not constitute a waiver of such right or provision.

If any provision of Terms is held by a court or other tribunal of competent jurisdiction to be invalid, illegal or unenforceable for any reason, such provision shall be eliminated or limited to the minimum extent such that the remaining provisions of Terms will continue in full force and effect.

## Acknowledgment

BY USING SERVICE OR OTHER SERVICES PROVIDED BY US, YOU ACKNOWLEDGE THAT YOU HAVE READ THESE TERMS OF SERVICE AND AGREE TO BE BOUND BY THEM.

## Contact Us

Please send your feedback, comments, requests for technical support [here](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur).

{% hint style="info" %}

## Changelog

#### 02 Dec 2025

* Refund policy updated.

#### 27 Jan 2023

* Removed: 'However, by posting Content using Service you grant us the right and license to use, modify, publicly perform, publicly display, reproduce, and distribute such Content on and through Service. You agree that this license includes the right for us to make your Content available to other users of Service, who may also use your Content subject to these Terms.'
  {% endhint %}


# Cookie Policy

Last updated January 31, 2022

This Cookie Policy explains how Thalia Technologies Private Limited ("**Company**", "**we**", "**us**", "**our**", "**csvbox.io**") uses cookies and similar technologies to recognize you when you visit our websites at <http://csvbox.io>, <http://app.csvbox.io/> ("**Websites**"). It explains what these technologies are and why we use them, as well as your rights to control our use of them.

## **What are cookies?**

Cookies are small data files that are placed on your computer or mobile device when you visit a website. Cookies are widely used by website owners in order to make their websites work, or to work more efficiently, as well as to provide reporting information.

Cookies set by the website owner (in this case, Thalia Technologies Private Limited) are called "first party cookies". Cookies set by parties other than the website owner are called "third party cookies". Third party cookies enable third party features or functionality to be provided on or through the website (e.g. like advertising, interactive content and analytics). The parties that set these third party cookies can recognize your computer both when it visits the website in question and also when it visits certain other websites.

\
**Why do we use cookies?**
--------------------------

We use first and third party cookies for several reasons. Some cookies are required for technical reasons in order for our Websites to operate, and we refer to these as "essential" or "strictly necessary" cookies. Other cookies also enable us to track and target the interests of our users to enhance the experience on our Online Properties. Third parties serve cookies through our Websites for advertising, analytics and other purposes. <br>

## **How can I control cookies?**

You have the right to decide whether to accept or reject cookies. You can exercise your cookie rights by setting your preferences in the Cookie Consent Banner. The Cookie Consent Manager allows you to accept or reject the cookies. Essential cookies will not be rejected as they are strictly necessary to provide you with services.

The Cookie Consent Banner can be found on our website. If you choose to reject cookies, you may still use our website though your access to some functionality and areas of our website may be restricted. You may also set or amend your web browser controls to accept or refuse cookies. As the means by which you can refuse cookies through your web browser controls vary from browser-to-browser, you should visit your browser's help menu for more information.

In addition, most advertising networks offer you a way to opt-out of targeted advertising. If you would like to find out more information, please visit <http://www.aboutads.info/choices/> or <http://www.youronlinechoices.com>.<br>

## **What about other tracking technologies, like web beacons?**

Cookies are not the only way to recognize or track visitors to a website. We may use other, similar technologies from time to time, like web beacons (sometimes called "tracking pixels" or "clear gifs"). These are tiny graphics files that contain a unique identifier that enable us to recognize when someone has visited our Websites or opened an e-mail including them. This allows us, for example, to monitor the traffic patterns of users from one page within a website to another, to deliver or communicate with cookies, to understand whether you have come to the website from an online advertisement displayed on a third-party website, to improve site performance, and to measure the success of e-mail marketing campaigns. In many instances, these technologies are reliant on cookies to function properly, and so declining cookies will impair their functioning.

## **Do you use Flash cookies or Local Shared Objects**

Websites may also use so-called "Flash Cookies" (also known as Local Shared Objects or "LSOs") to, among other things, collect and store information about your use of our services, fraud prevention and for other site operations.\
If you do not want Flash Cookies stored on your computer, you can adjust the settings of your Flash player to block Flash Cookies storage using the tools contained in the [Website Storage Settings Panel](http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager07.html). You can also control Flash Cookies by going to the [Global Storage Settings Panel](http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager03.html) and following the instructions (which may include instructions that explain, for example, how to delete existing Flash Cookies (referred to "information" on the Macromedia site), how to prevent Flash LSOs from being placed on your computer without your being asked, and (for Flash Player 8 and later) how to block Flash Cookies that are not being delivered by the operator of the page you are on at the time).\
Please note that setting the Flash Player to restrict or limit acceptance of Flash Cookies may reduce or impede the functionality of some Flash applications, including, potentially, Flash applications used in connection with our services or online content.

## **Do you serve targeted advertising?**

Third parties may serve cookies on your computer or mobile device to serve advertising through our Websites. These companies may use information about your visits to this and other websites in order to provide relevant advertisements about goods and services that you may be interested in. They may also employ technology that is used to measure the effectiveness of advertisements. This can be accomplished by them using cookies or web beacons to collect information about your visits to this and other sites in order to provide relevant advertisements about goods and services of potential interest to you. The information collected through this process does not enable us or them to identify your name, contact details or other details that directly identify you unless you choose to provide these.

## **How often will you update this Cookie Policy?**

We may update this Cookie Policy from time to time in order to reflect, for example, changes to the cookies we use or for other operational, legal or regulatory reasons. Please therefore re-visit this Cookie Policy regularly to stay informed about our use of cookies and related technologies.

\
The date at the top of this Cookie Policy indicates when it was last updated.


# User Data Policy

Revision Date: 06 April 2023

## Data Retention

Ensuring the privacy and security of user data is a top priority for us. You can rest easy, knowing that we take every precaution to provide an import service with high-grade security.

We provide a variety of data processing modes, including the [**Private Mode**](/destinations/private-mode). The following is the description of the Standard Mode.

When a user submits a file, CSVbox parses the file on the client side and then sends it to our servers. The data in transit is fully protected with a 256-bit SSL (Secure Socket Layer) connection that uses a SHA256 Certificate. This is the same level of protection used by online banking or e-commerce providers. &#x20;

We use databases from Amazon Web Services (AWS) with encryption enabled by default. The uploaded data is secured at rest and no one else can read it while it resides in our storage. Once the data is fully stored in our database, CSVbox then pushes it to your app or any other destination as configured by you in the dashboard.

<figure><img src="/files/O8kXxWW9dgrljSs8Bz2z" alt=""><figcaption><p>Data flow in Standard Mode</p></figcaption></figure>

The user data from our storage is later deleted automatically after one month. Meanwhile, you can securely view and download this data anytime via the csvbox.io dashboard. We provide the option to get all the user-uploaded data deleted from our database (AWS S3) anytime with the click of a button. Lastly, you can configure the importer to completely bypass storing the data in our database (AWS S3).   &#x20;

The long-lived data about the import and the user files is not deleted. It mainly consists of supplementary log data helpful for troubleshooting and analyzing the import processes. This data does not include any original data from inside the user files.

## Data Residency

Data residency refers to where the data is stored in a geographical location. The location is important usually for regulatory or policy reasons. The data, uploaded by your users, goes through our servers and gets stored in our databases all located in the US by default.

We offer the option to configure the data residency location to US and Europe (Germany).

## Zero-knowledge design

csvbox.io collects, processes and stores only information that is required to run the service. We do not proactively build customer profiles, track metadata about the end-users or store unnecessary information in cookies unless our clients explicitly demand it to achieve their business goals.

With privacy at heart, we appreciate that the same business objectives can be achieved with multiple approaches. Processing unnecessary information represents a liability for us and risk for our clients, therefore by default, we prefer to design our features with a zero-knowledge approach.

If you have any specific questions you may raise a ticket [here](https://share.hsforms.com/1ubpg6RBoQgKOISkRMEViwg5auur).

## **GDPR** <a href="#gdpr" id="gdpr"></a>

* CSVbox.io is committed to users' right to data privacy and respects the spirit of the EU's General Data Protection Regulation.
* We now have a GDPR compliant [Data Processing Agreement (DPA)](/legal/gdpr/data-processing-addendum), that our customers can optionally sign with us.
* We're continuously working on adding more data control options (for activity log data and others).
* More information on our GDPR readiness is available [here](/legal/gdpr).

{% hint style="info" %}

## User Data Policy Changelog

#### 06 April 2023

* Added text:

We provide a variety of data processing modes, including the [**Private Mode**](/destinations/private-mode). The following is the description of the Standard Mode.
{% endhint %}


# GDPR

At CSVbox we take data protection and privacy seriously. We firmly believe in respecting our customers and their respective users’ privacy rights.

## Introduction

The GDPR enforcement puts the control of personal data, collected by businesses, in the hands of the individuals, it belongs to, protecting the rights of EU residents.

The regulation delineates individuals’ rights to [access, rectify, and restrict](https://gdpr-info.eu/chapter-3/) the processing of personal data, among other key provisions, and aims to unify privacy and security laws for all organizations operating within the EU.

In the context of this user document, we will be focused on how to implement the different rights once invoked by the Data Subjects.

## Nomenclature

* Data Subject: End Users
* Data Controller: CSVbox Customers
* Data Processor: CSVbox

## Documents

#### 1. [Data Processing Agreement (DPA)](/legal/gdpr/data-processing-addendum)  &#x20;

#### 2. [Terms of Use ](/legal/terms#content)

#### 3. [Privacy Policy](/legal/privacy)

## Data Subject Rights

### The Right to Access

Under GDPR, individuals have the right to obtain:

* Confirmation that their data is being processed;
* Access to their personal data; and
* Other supplementary information – this largely corresponds to the information that should be provided in a privacy notice (see [GDPR Article 15](https://gdpr-info.eu/art-15-gdpr/)).

**CSVbox Compliance**

CSVbox enables you to download the data files uploaded by the users via the CSVbox dashboard. You can then provide this data to the Data Subject in response to their request to access any personal data being processed by CSVbox as a Data Processor on your behalf. If you have disabled file storage in CSVbox then the data files will not be available.

### The Right to Rectification

Individuals are entitled to have personal data rectified if it is inaccurate or incomplete. If you have disclosed the personal data in question to third parties, you must inform them of the rectification where possible.

**CSVbox Compliance**

If a Data Subject requests that you rectify inaccuracies within the personal data being processed by CSVbox on your behalf, you can delete the old files and request the Data Subject to re-upload the files using the CSVbox importer.

### The Right to Erasure

Individuals have the right to get personal data concerning them erased by the controllers. The right to erasure is also known as 'the right to be forgotten'.

**CSVbox Compliance**

You can delete the data files from the CSVbox dashboard if Data Subjects request so.

{% hint style="info" %}
CSVbox automatically deletes all user data files 30 days after upload.
{% endhint %}

### The Right to Data Suppress

This right allows users to opt-out of sharing any data with Data Processors.

**CSVbox Compliance**

If you have been asked by the Data Subject to restrict the processing of their data, you can simply stop the CSVbox importer initialization or ask the Data Subjects not to submit their files.

### The Right to Data Portability

The right to data portability allows individuals to obtain and reuse their personal data for their own purposes across different services.

**CSVbox Compliance**

You may use the CSVbox dashboard to download the data files and furnish it to the Data Subject pursuant to his/her request.

## Is GDPR applicable to me?

Under the GDPR, it is the location of the individual whose personal data is being processed that determines whether the concerned firm should comply. This means that the GDPR will apply to all organizations, whether within the EU or outside of it, that offer their product or service to individuals in the EU when their data is being collected.&#x20;

That said, your legal/compliance teams will be in a better position to answer if your app/business falls under the purview of GDPR regulations.

## Our commitment to GDPR

We are fully committed to GDPR and hence have built product features for greater privacy and data control. As an organization, we have always implemented and practiced processes that ensure that customer data is stored and processed in ways necessary only to serve our customers in the best possible way. Our privacy, security, and data policies are also streamlined with the GDPR goals and objectives.


# Data Processing Addendum

Last Modified: April 22, 2026

{% hint style="info" %}
Summary of main changes

April 22, 2026

* Added Azure as Sub Processor.

October 17, 2025

* Added Sengrid as Sub Processor.
* Added Brevo as Sub Processor.
* Added PostHog as Sub Processor.
* Removed Sender.net as Sub Processor.
* Removed Hotjar as Sub Processor.

Mar 21, 2024

* Added OpenAI as Sub Processor.

Nov 21, 2022

* Removed ArvanCloud as Sub Processor.

February 20, 2022

* Document created.
  {% endhint %}

This Data Processing Agreement (“DPA”) is for the product named CSVbox offered by Thalia Technologies Private Limited. This agreement includes the Standard Contractual Clauses adopted by the European Commission, as applicable, and reflects the parties’ agreement with respect to the terms governing the Processing of Personal Data under the CSVBox [Terms of Use](https://help.csvbox.io/legal/terms). This DPA is an amendment to the Agreement and is effective upon its incorporation into the Agreement, which incorporation may be specified in the Agreement, an Order, or an executed amendment to the Agreement. Upon its incorporation into the Agreement, the DPA will form a part of the Agreement.

The term of this DPA shall follow the term of the Agreement. Terms not otherwise defined herein shall have the meaning as set forth in the Agreement.

### Definitions

“**Controller**” means the natural or legal person, public authority, agency or other body which, alone or jointly with others, determines the purposes and means of the processing of Personal Data.

“**Data Protection Law**” means all applicable legislation relating to data protection and privacy including without limitation the EU Data Protection Directive 95/46/EC and all local laws and regulations which amend or replace any of them, including the GDPR, together with any national implementing laws in any Member State of the European Union or, to the extent applicable, in any other country, as amended, repealed, consolidated or replaced from time to time. The terms “process”, “processes” and “processed” will be construed accordingly.

“**Data Subject**” means the individual to whom Personal Data relates.

“**GDPR**” means the General Data Protection Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 on the protection of natural persons with regard to the processing of personal data and on the free movement of such data.

“**Instruction**” means the written, documented instruction, issued by the Controller to the Processor, and directing the same to perform a specific action with regard to Personal Data (including, but not limited to, depersonalizing, blocking, deletion, making available).

“**Personal Data**” means any information relating to an identified or identifiable individual where such information is contained within Customer Data and is protected similarly as personal data or personally identifiable information under applicable Data Protection Law.

“**Personal Data Breach**” means a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to, Personal Data transmitted, stored, or otherwise processed.

“**Processing**” means any operation or set of operations that is performed on Personal Data, encompassing the collection, recording, organization, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction or erasure of Personal Data.

“**Processor**” means a natural or legal person, public authority, agency, or other body which processes Personal Data on behalf of the Controller.

### Subject Matter and Nature of Processing

The subject matter of Processing Personal Data by the Processor is the provision of the services to the Controller that involves the Processing of Personal Data. Personal Data will be subject to those Processing activities as may be specified in the Agreement and an Order.&#x20;

### Types of Personal Data and Purpose of Processing

Contact information, the extent of which is determined and controlled by the Customer in its sole discretion, and other Personal Data such as navigational data (including website usage information), email data, system usage data, application integration data, and other electronic data submitted, stored, sent, or received by end-users via the CSVBox Product. Personal Data will be processed for purposes of providing the services set out and otherwise agreed to in the Agreement and any applicable Order.

### Categories of Data Subjects

The Controller’s Contacts and other end users including the Controller’s employees, contractors, collaborators, customers, prospects, suppliers, and subcontractors. Data Subjects also include individuals attempting to communicate with or transfer Personal Data to the Controller’s end users.&#x20;

### Customer Responsibility

The Controller shall be solely responsible for complying with the statutory requirements relating to data protection and privacy, in particular regarding the disclosure and transfer of Personal Data to the Processor and the Processing of Personal Data. For the avoidance of doubt, the Controller’s instructions for the Processing of Personal Data shall comply with the Data Protection Law. The Controller shall inform the Processor without undue delay and comprehensively about any errors or irregularities related to statutory provisions on the Processing of Personal Data.&#x20;

### Obligations of the Processor

The Processor shall collect, process, and use Personal Data only within the scope of the Controller’s instructions. If the Processor believes that an Instruction of the Controller infringes the Data Protection Law, it shall immediately inform the Controller without delay. If the Processor cannot process Personal Data in accordance with the instructions due to a legal requirement under any applicable European Union or Member State law, the Processor will (i) promptly notify the Controller of that legal requirement before the relevant Processing to the extent permitted by the Data Protection Law; and (ii) cease all Processing (other than merely storing and maintaining the security of the affected Personal Data) until such time as the Controller issues new instructions with which the Processor is able to comply.

The Processor shall take the appropriate technical and organizational measures to adequately protect Personal Data against accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to Personal Data. Such measures include, but are not be limited to:

1. the prevention of unauthorized persons from gaining access to Personal Data Processing systems (physical access control),
2. the prevention of Personal Data Processing systems from being used without authorization (logical access control),&#x20;
3. ensuring that Personal Data cannot be read, copied, modified or deleted without authorization during electronic transmission, transport or storage on storage media, and that the target entities for any transfer of Personal Data by means of data transmission facilities can be established and verified (data transfer control),
4. ensuring that Personal Data is processed solely in accordance with the Instructions (control of instructions),&#x20;
5. ensuring that Personal Data is protected against accidental destruction or loss (availability control).

### Rectification, Restriction, and Erasure of Data

The Processor will provide reasonable assistance, including by appropriate technical and organizational measures and taking into account the nature of the Processing, to enable the Controller to respond to any request from Data Subjects seeking to exercise their rights under the Data Protection Law with respect to Personal Data (including access, rectification, restriction, deletion or portability of Personal Data, as applicable), to the extent permitted by the law. If such a request is made directly to the Processor, the Processor will promptly inform the Controller and will advise Data Subjects to submit their request to the Controller. The Controller shall be solely responsible for responding to any Data Subjects’ requests. The Controller shall reimburse the Processor for the costs arising from this assistance.

### Data Breaches

The Processor will notify the Controller as soon as practicable after it becomes aware of any Personal Data Breach affecting any Personal Data. At the Controller’s request, the Processor will promptly provide the Controller with all reasonable assistance necessary to enable the Controller to notify relevant Personal Data Breaches to competent authorities and/or affected Data Subjects, if the Controller is required to do so under the Data Protection Law.

### Sub Processors

The Processor shall be entitled to engage sub-processors to fulfill the Processor’s obligations defined in the Agreement by way of the controller agreeing to the terms of service of CSVBox. Where the Processor engages sub-processors, the Processor will engage only with sub-processors whose terms of service honor the similar obligations that apply to the Processor under this DPA.

The Processor hosts its Service with outsourced cloud infrastructure provider, Amazon Web Services (AWS). The servers and the databases are located in the US and optionally Europe.

Additionally, here is the list of Sub-Processors:

1. Cloudflare: CDN and Security
2. Zoho: Email & Collaboration
3. Google Workspace: Business Apps & Collaboration Tools
4. Paddle: Payments
5. Rollbar: Error Logging
6. Hubspot: CRM
7. OpenAI: AI Services
8. Sendgrid: Emails
9. Brevo: Emails
10. Posthog: Analytics
11. Azure: Document Intelligence

### Governing Law and Jurisdiction

This Agreement is governed by Indian law. Any dispute arising in connection with this Agreement, which the Parties will not be able to resolve amicably, will be submitted to the exclusive jurisdiction of the courts of Mumbai, subject to a possible appeal to the Indian Supreme Court in Delhi.


# SOC 2 Type 2

Find out how CSVbox is setting the benchmark for the privacy and confidentiality of customer data through its SOC 2 Type 2 compliance.

### What is SOC2?

SOC 2 is a set of standards devised by the American Institute of Certified Public Accountants (AICPA) for organizations that handle sensitive customer data. SOC stands for Service Organization Control, and SOC 2 specifically relates to security, availability, processing integrity, confidentiality, and privacy.\
‍\
A third-party auditor issues the SOC 2 report and assesses whether an organization's systems and controls meet the requisite standards. It is increasingly becoming a standard for companies dealing with sensitive customer data and is often required by customers, partners, and regulators across industries.

### **Types of SOC2**

#### Type 1 Report

An assessment of the system and controls a company has set up, at a specific point in time, with regard to security, privacy, processing integrity, and confidentiality of data. It provides an initial assurance that this company has proper controls available to protect sensitive data.

#### Type 2 Report

This focuses on the operating effectiveness of the controls over a period of time. It provides a more comprehensive assessment of a company's security controls and is often required to safely handle sensitive information.

{% hint style="info" %}
**CSVbox became SOC 2 Type 2 compliant on 12th October 2023.**
{% endhint %}

### Features of CSVbox's SOC2 Type 2 compliance

As CSVbos builds the platform to allow the upload of spreadsheets, we must have the right tooling to handle this data. Here’s how SOC 2 Type 2 compliance will play a key role in this:

* #### Enhanced Security

  With robust controls to protect sensitive and confidential information, we help reduce the risk of data breaches, unauthorized access, and unwarranted security incidents.
* #### Increased Trust and Credibility

  The SOC 2 certification demonstrates to our customers, partners, and other stakeholders that we take security, data protection, and legal compliance seriously.&#x20;
* #### Improved Risk Management

  As SOC 2 compliance required us to identify and assess potential risks to our systems and data, we are now better equipped to mitigate and respond to potential threats.
* #### Continuous Improvement

  Regular audits are required to maintain the SOC2 Type 2 certification, thereby guaranteeing that we will continue to stay updated with security best practices.
* #### Better Incident Response

  With this compliance, we have incident response plans that can help minimize the impact of security incidents.

At CSVbox, we understand the importance of data security and privacy, and we are dedicated to ensuring that our customers' information is protected. Our SOC2 Type 2 compliance means that we have established the necessary security controls and processes to protect customer data.

Achieving SOC2 Type 2 compliance is just one of the many steps we are taking to ensure the security and privacy of our customer's data. We will continue to evaluate and enhance our security controls and processes to ensure that we meet our customers' ever-evolving security and privacy needs.


