# Create a custom variable

Source: https://docs.emelia.io/docs/lists/create-custom-variable

Guide: [Lists & contacts](https://docs.emelia.io/docs/lists.md)

`POST https://api.emelia.io/lists/custom-variable`

Declares a custom variable on the account, so that contact keys with this name (technical or display name) land in it instead of being guessed. Declare your variables before loading contacts. This is the only way to create a COMPANY custom variable: the contact routes only create contact (`lead`) variables.

Naming rule: a technical name that is already a clean identifier (letter first, then letters, digits or `_`) is kept, with its first letter lowercased (`basile_gmb_name` stays `basile_gmb_name`). Anything else is camelCased with accents and symbols dropped (`Ice breaker` becomes `iceBreaker`). The stored name is returned.

The call is refused (422) when a variable with the same name already exists for this schema (comparison ignoring case, spaces, `_` and `-`) or when the name is a standard field of the schema: it is therefore safe to call it every time and treat 422 as already existing. Variables are shared by all your lists and cannot be renamed or deleted through the API.

## Authentication

Send your API key in the `Authorization` header: `Authorization: <your API key>`.

## Request body

Content type: `application/json`, required.

| Field | Type | Required | Description |
|---|---|---|---|
| `technicalName` | string | yes | Name used as the contact key and in `{{variables}}`. Normalized (see the route description). Min length 1. |
| `schema` | enum: `lead`, `company` | yes | `lead`: stored on the contact. `company`: stored on the company row of the contact. |
| `displayName` | string | no | Label shown in the app. Defaults to the technical name. |

```json
{
  "technicalName": "icebreaker",
  "schema": "lead",
  "displayName": "Icebreaker"
}
```

## Example request

**cURL**

```bash
curl -X POST "https://api.emelia.io/lists/custom-variable" \
  -H "Authorization: $EMELIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "technicalName": "icebreaker",
  "schema": "lead",
  "displayName": "Icebreaker"
}'
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/lists/custom-variable', {
  method: 'POST',
  headers: { Authorization: process.env.EMELIA_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "technicalName": "icebreaker",
    "schema": "lead",
    "displayName": "Icebreaker"
  }),
});
console.log(await response.json());
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.emelia.io/lists/custom-variable",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
    json={
        "technicalName": "icebreaker",
        "schema": "lead",
        "displayName": "Icebreaker"
    },
)
print(response.json())
```

## Responses

### 201: Variable created.

| Field | Type | Required | Description |
|---|---|---|---|
| `technicalName` | string | yes | Name as stored after normalization. |
| `displayName` | string | yes |  |
| `schema` | enum: `lead`, `company` | yes |  |

```json
{
  "technicalName": "icebreaker",
  "displayName": "Icebreaker",
  "schema": "lead"
}
```

### 401: Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.

| Field | Type | Required | Description |
|---|---|---|---|
| `error` | string | yes |  |
| `code` | enum: `AUTH_REQUIRED`, `ACCOUNT_NOT_FOUND` | yes |  |

```json
{
  "error": "string",
  "code": "AUTH_REQUIRED"
}
```

### 422: Invalid body, empty name after normalization, existing variable or standard field.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "string",
  "code": "string",
  "params": {},
  "error": "string"
}
```

### 500: Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "Hmm... Something went wrong. Please try again later."
}
```
