> ## Documentation Index
> Fetch the complete documentation index at: https://developers.benchmarkemail.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage custom fields

# Manage Custom Fields

View and update the contact structure that determines which fields are available on your contacts.

## Goal

By the end of this guide you will be able to retrieve your contact structure with its fields and tags, and update it to add or modify custom fields.

## Prerequisites

* An API key with **`contacts:write`** scope (for updating the structure) or **`contacts:read`** scope (for read-only access)
* Your API base URL (found on the Settings > API Keys page)

## Background

A **contact structure** defines the schema for your contacts. It includes:

* A **key name** and **key type** — the primary identifier for contacts (typically "Email" of type "email")
* **Fields** — custom fields like First Name, Last Name, Company, Phone, etc.
* **Tags** — label definitions that can be assigned to contacts

Every contact belongs to exactly one contact structure. Your account has a default contact structure that was created when the account was set up.

**Note:** Creating new contact structures via API key is not supported. Use the Benchmark Email web application to create additional structures if needed. API keys can read and update existing structures.

## Steps

### 1. List all contact structures

Retrieve all contact structures in your account.

```bash theme={null}
curl https://api-us-west-2-c1.benchmarkemail.com/api/contact-structure \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
```

**Response** (`200 OK`):

```json theme={null}
[
  {
    "_id": "64a1b2c3d4e5f6a7b8c9d0e1",
    "label": "Default Contacts",
    "keyName": "Email",
    "keyType": "email",
    "fields": [
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d100",
        "label": "First Name",
        "dataType": "text",
        "required": false,
        "predefinedField": "firstName"
      },
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d101",
        "label": "Last Name",
        "dataType": "text",
        "required": false,
        "predefinedField": "lastName"
      },
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d102",
        "label": "Company",
        "dataType": "text",
        "required": false
      }
    ],
    "tags": [
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d300",
        "label": "VIP"
      },
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d301",
        "label": "Prospect"
      }
    ]
  }
]
```

**Key takeaway:** The `_id` values of the fields array are what you use when creating or updating contacts and when searching by custom field values.

### 2. Get a specific contact structure

Retrieve a single contact structure by its ID.

```bash theme={null}
curl https://api-us-west-2-c1.benchmarkemail.com/api/contact-structure/64a1b2c3d4e5f6a7b8c9d0e1 \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
```

**Response** (`200 OK`):

```json theme={null}
{
  "_id": "64a1b2c3d4e5f6a7b8c9d0e1",
  "label": "Default Contacts",
  "keyName": "Email",
  "keyType": "email",
  "fields": [
    {
      "_id": "64a1b2c3d4e5f6a7b8c9d100",
      "label": "First Name",
      "dataType": "text",
      "required": false,
      "predefinedField": "firstName"
    },
    {
      "_id": "64a1b2c3d4e5f6a7b8c9d101",
      "label": "Last Name",
      "dataType": "text",
      "required": false,
      "predefinedField": "lastName"
    },
    {
      "_id": "64a1b2c3d4e5f6a7b8c9d102",
      "label": "Company",
      "dataType": "text",
      "required": false
    }
  ],
  "tags": [
    { "_id": "64a1b2c3d4e5f6a7b8c9d300", "label": "VIP" },
    { "_id": "64a1b2c3d4e5f6a7b8c9d301", "label": "Prospect" }
  ],
  "__v": 3
}
```

### 3. Update a contact structure

Use `PUT` to update a contact structure. This replaces the full structure, so include all existing fields plus any changes. Include the current `__v` value for optimistic concurrency control.

Adding a new "Loyalty Tier" field to an existing structure:

```bash theme={null}
curl -X PUT https://api-us-west-2-c1.benchmarkemail.com/api/contact-structure/64a1b2c3d4e5f6a7b8c9d0e1 \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Default Contacts",
    "keyName": "Email",
    "fields": [
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d100",
        "label": "First Name",
        "dataType": "text",
        "required": false,
        "predefinedField": "firstName"
      },
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d101",
        "label": "Last Name",
        "dataType": "text",
        "required": false,
        "predefinedField": "lastName"
      },
      {
        "_id": "64a1b2c3d4e5f6a7b8c9d102",
        "label": "Company",
        "dataType": "text",
        "required": false
      },
      {
        "label": "Loyalty Tier",
        "dataType": "text",
        "required": false
      }
    ],
    "tags": [
      { "_id": "64a1b2c3d4e5f6a7b8c9d300", "label": "VIP" },
      { "_id": "64a1b2c3d4e5f6a7b8c9d301", "label": "Prospect" }
    ],
    "__v": 3
  }'
```

**Response** (`200 OK`):

```json theme={null}
{
  "_id": "64a1b2c3d4e5f6a7b8c9d0e1",
  "label": "Default Contacts",
  "keyName": "Email",
  "keyType": "email",
  "fields": [
    { "_id": "64a1b2c3d4e5f6a7b8c9d100", "label": "First Name", "dataType": "text", "required": false, "predefinedField": "firstName" },
    { "_id": "64a1b2c3d4e5f6a7b8c9d101", "label": "Last Name", "dataType": "text", "required": false, "predefinedField": "lastName" },
    { "_id": "64a1b2c3d4e5f6a7b8c9d102", "label": "Company", "dataType": "text", "required": false },
    { "_id": "64a1b2c3d4e5f6a7b8c9d103", "label": "Loyalty Tier", "dataType": "text", "required": false }
  ],
  "tags": [
    { "_id": "64a1b2c3d4e5f6a7b8c9d300", "label": "VIP" },
    { "_id": "64a1b2c3d4e5f6a7b8c9d301", "label": "Prospect" }
  ],
  "__v": 4
}
```

**Supported field data types:**

* `text` — free-text string
* `number` — numeric value
* `date` — date value
* `boolean` — true/false
* `recurrent_date` — recurring date (month/day)

**Predefined fields:** Use `predefinedField` to map standard fields like `firstName`, `lastName`. These enable features like merge tags in email campaigns.

**Important notes:**

* `PUT` is a full replacement. Include all existing fields you want to keep, or they will be removed.
* When adding a new field, omit `_id` — the server generates it.
* When keeping existing fields, include their `_id` to preserve them.
* Include the current `__v` value for optimistic concurrency control.
* Removing a field that is referenced by a published signup form will return a `400` error with details about which forms are affected.

## Common Errors

| Status | Error                    | Cause                                                                         | Fix                                                                                                                    |
| ------ | ------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `401`  | `UnauthorizedError`      | Invalid or inactive API key                                                   | Verify your key in Settings > API Keys                                                                                 |
| `403`  | `ForbiddenError`         | Key lacks `contacts:write` scope                                              | Update operations require `contacts:write`                                                                             |
| `403`  | `ForbiddenError`         | Attempted to create a new contact structure via API key                       | Creating contact structures is not available via API key. Use the web application                                      |
| `400`  | `ValidationError`        | Missing required properties (`label`, `keyName`), or invalid field `dataType` | Check request body against the schema requirements                                                                     |
| `400`  | `ContactFieldInUseError` | Tried to remove a field used by a signup form                                 | Update or unpublish the affected form first. The error response includes `debug.affectedForms` with form IDs and names |
| `404`  | `RecordNotFound`         | Contact structure ID does not exist                                           | Verify the structure ID                                                                                                |
| `429`  | `TooManyRequestsError`   | Rate limit exceeded                                                           | Wait for the `Retry-After` period. See [Rate Limits](../rate-limits.md)                                                |

## Next Steps

* [Manage Contacts](./manage-contacts.md) — create contacts using the field IDs from your structure
* [Manage Lists](./manage-lists.md) — create lists within a contact structure
* [Search Contacts](./search-contacts.md) — search by custom field values
* [Migration from Legacy](./migration-from-legacy.md) — set up your structure before importing data
