> ## 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.

# Authentication

> Authenticate API requests with an API key and manage permissions via scopes.

All API requests must include a valid API key in the `X-API-Key` header.

```bash theme={null}
curl -H "X-API-Key: bme_us_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v" \
  "<your-api-base-url>/api/contact-structure"
```

## Required Headers

Every request must include both of the following headers:

| Header | Required | Description |
| - | - | - |
| `X-API-Key` | Yes | Your API key. See [Getting an API Key](#getting-an-api-key). |
| `User-Agent` | Yes | Identifies your application, for example `MyIntegration/1.0`. Requests without a `User-Agent` header are rejected before they reach the API. |

### User-Agent

Benchmark Email's edge protection blocks any request that does not carry a `User-Agent` header. The block happens before the request reaches the API, so the response is a `403 Forbidden` with an HTML body rather than the usual JSON error format. If you see an HTML `403` page while your key and scopes are correct, a missing `User-Agent` header is the most likely cause.

Most HTTP clients send a `User-Agent` automatically, including the `curl` command line, browsers, and the standard HTTP libraries in Python, Node.js, Ruby, Go, and Java. Some clients do not send one unless you set it explicitly. PHP's cURL extension is the most common example:

```php theme={null}
$ch = curl_init("<your-api-base-url>/api/contact-structure");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "MyIntegration/1.0");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "X-API-Key: bme_us_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v",
]);
$result = curl_exec($ch);
```

Use a value that names your application or integration. Any non-empty value is accepted.

## Getting an API Key

1. Log in to Benchmark Email with an **Owner** account. Only users with the Owner role can create and manage API keys.
2. Navigate to **Settings > API Keys**.
3. Click **Create API Key**.
4. Enter a descriptive name (e.g., "Zapier Sync", "CRM Integration").
5. Select the scopes (permissions) the key needs -- see [Scopes](#scopes) below.
6. Optionally set an expiration date. If you skip this, the key never expires.
7. Click **Create** and copy the key immediately.

The full API key is displayed **only once** at creation time. If you lose it, you can regenerate the key from the API Keys page (this invalidates the old key and issues a new one).

## API Key Format

All Benchmark Email API keys are 50 characters long. Each key starts with `bme_` followed by a 2-letter region code (such as `us`, `jp`, or `eu`) and 43 random characters:

```
bme_us_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v
```

The region code reflects your account's region and is set automatically when the key is created. You do not need to do anything with it -- use the key exactly as shown on the API Keys page.

## Scopes

Each API key is granted one or more scopes that control which resources it can access. Scopes follow the `{resource}:{access}` format.

### Available Scopes

| Scope | Description |
| - | - |
| `contacts:read` | Read contacts, lists, contact structures, search contacts, export contacts, view contact events and history |
| `contacts:write` | Create, update, and delete contacts and lists; update contact structures |
| `campaigns:read` | Read campaigns and browse email templates |
| `campaigns:write` | Create, update, delete, and duplicate campaigns |
| `reports:read` | View dashboard summaries and email performance reports |
| `domains:read` | View email sending domains |

### Write Implies Read

Granting **write** access for a resource automatically includes **read** access. For example, a key with `contacts:write` can also read contacts -- you do not need to select both.

### Principle of Least Privilege

Create keys with only the permissions they need. For example:

* A reporting dashboard only needs `reports:read`.
* A contact sync integration needs `contacts:write` (which includes read access).
* A read-only data export tool needs `contacts:read`.

### Scope Errors

If a request requires a scope that your key does not have, you will receive a `403 Forbidden` response with a message identifying the required scope. See [Errors](/errors#missing-required-scope-403) for details.

## Account Standing

API keys only work when your Benchmark Email account is in good standing. Keys are active when your account status is:

* **Open** -- normal active account
* **Pending Cancel** -- account is scheduled for cancellation but still active

Keys will stop working (returning `403 Forbidden`) if your account is in any other status, such as suspended, past due, or terminated.

## Key Lifecycle

| Key State | Behavior |
| - | - |
| **Active** | Key authenticates requests normally |
| **Inactive** | Key has been deactivated by the owner; returns `401 Unauthorized` |
| **Expired** | Key's expiration date has passed; returns `401 Unauthorized` |
| **Deleted** | Key has been permanently removed; returns `401 Unauthorized` |

You can deactivate and reactivate keys from the API Keys page without deleting them. This is useful for temporarily disabling an integration.

## Example: Listing Contact Structures

```bash theme={null}
curl -X GET \
  -H "X-API-Key: bme_us_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v" \
  "<your-api-base-url>/api/contact-structure"
```

**Response (200 OK):**

```json theme={null}
[
  {
    "_id": "64a1b2c3d4e5f6a7b8c9d0e1",
    "label": "Default Contacts",
    "keyName": "Email",
    "keyType": "email",
    "fields": [
      { "_id": "64a1b2c3d4e5f6a7b8c9d100", "label": "First Name", "dataType": "text" },
      { "_id": "64a1b2c3d4e5f6a7b8c9d101", "label": "Last Name", "dataType": "text" }
    ]
  }
]
```

## Example: Creating a Contact

```bash theme={null}
curl -X POST \
  -H "X-API-Key: bme_us_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "john.smith@example.com",
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "fields": [
      { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "John" },
      { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Smith" }
    ]
  }' \
  "<your-api-base-url>/api/contact"
```

**Response (200 OK):**

```json theme={null}
{
  "_id": "65a1b2c3d4e5f6a7b8c9d0e2",
  "key": "john.smith@example.com",
  "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
  "fields": [
    { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "John" },
    { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Smith" }
  ],
  "status": { "primary": "active", "secondary": "confirmed" },
  "createdAt": "2026-03-30T14:22:00.000Z"
}
```

This request requires the `contacts:write` scope. If your key only has `contacts:read`, you will receive a `403 Forbidden` error.

## Next Steps

* [Rate Limits](/rate-limits) -- understand request limits and quotas
* [Errors](/errors) -- handle error responses
* [API Reference](/api-reference) -- explore available resources


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.