Skip to main content
API keys allow external services and scripts to authenticate with the Naturalead API without a user session. Each key is scoped to an account and restricted to a specific set of RBAC permissions.

Key concepts

Scoped permissions

Each key is granted a subset of RBAC permissions. A key creator can only grant permissions they themselves hold.

Environment isolation

Keys are either live or test. Live keys use the prefix nl_live_ and test keys use nl_test_.

Shown once

The full key is returned only at creation (or rotation). It is hashed with SHA-256 before storage and can never be retrieved again.

Audit trail

All key lifecycle events (create, revoke, rotate, delete) are logged to the audit log for SOC2 compliance.

Step-by-step

1
Create an API key
2
Choose a descriptive name, select the permissions the key needs, and pick the environment.
3
curl
curl -X POST "${API_URL}/api/api-keys" \
  -H "X-API-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM lead sync",
    "scopes": ["leads:view", "leads:import", "conversations:view"],
    "environment": "live",
    "expiresAt": "2027-01-01T00:00:00Z"
  }'
Python
import requests

headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
}

response = requests.post(
    f"{API_URL}/api/api-keys",
    headers=headers,
    json={
        "name": "CRM lead sync",
        "scopes": ["leads:view", "leads:import", "conversations:view"],
        "environment": "live",
        "expiresAt": "2027-01-01T00:00:00Z",
    },
)

data = response.json()
full_key = data["fullKey"]  # Save this - shown only once!
print(f"Key created: {data['apiKey']['keyPrefix']}...{data['apiKey']['keyHint']}")
Node.js
const response = await fetch(`${API_URL}/api/api-keys`, {
  method: "POST",
  headers: {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "CRM lead sync",
    scopes: ["leads:view", "leads:import", "conversations:view"],
    environment: "live",
    expiresAt: "2027-01-01T00:00:00Z",
  }),
});

const data = await response.json();
const fullKey = data.fullKey; // Save this - shown only once!
console.log(`Key created: ${data.apiKey.keyPrefix}...${data.apiKey.keyHint}`);
4
Store the fullKey value immediately. It is returned only once and cannot be retrieved later. If lost, you must rotate the key to get a new one.
5
Use the API key
6
Include the key in requests via the X-API-Key header or as a Bearer token.
7
X-API-Key header
curl "${API_URL}/api/leads" \
  -H "X-API-Key: nl_live_a1b2c3d4..."
Bearer token
curl "${API_URL}/api/leads" \
  -H "Authorization: Bearer nl_live_a1b2c3d4..."
8
List existing keys
9
View all keys for your account. Full key values are never returned — only the prefix and last 4 characters.
10
curl
curl "${API_URL}/api/api-keys" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.get(
    f"{API_URL}/api/api-keys",
    headers={"X-API-Key": API_KEY},
)

for key in response.json():
    print(f"{key['name']}: {key['keyPrefix']}...{key['keyHint']} ({key['status']})")
Node.js
const response = await fetch(`${API_URL}/api/api-keys`, {
  headers: { "X-API-Key": API_KEY },
});

const keys = await response.json();
keys.forEach((key) => {
  console.log(`${key.name}: ${key.keyPrefix}...${key.keyHint} (${key.status})`);
});
11
Rotate a key
12
Rotation atomically revokes the old key and creates a new one with the same name, scopes, and environment. The old key stops working immediately.
13
curl
curl -X POST "${API_URL}/api/api-keys/${KEY_ID}/rotate" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.post(
    f"{API_URL}/api/api-keys/{key_id}/rotate",
    headers={"X-API-Key": API_KEY},
)

data = response.json()
new_key = data["fullKey"]  # Save the new key!
print(f"Rotated: {data['apiKey']['keyPrefix']}...{data['apiKey']['keyHint']}")
Node.js
const response = await fetch(`${API_URL}/api/api-keys/${keyId}/rotate`, {
  method: "POST",
  headers: { "X-API-Key": API_KEY },
});

const data = await response.json();
const newKey = data.fullKey; // Save the new key!
console.log(`Rotated: ${data.apiKey.keyPrefix}...${data.apiKey.keyHint}`);
14
Revoke a key
15
Revoking a key disables it permanently. Revoked keys remain visible for audit purposes but can be deleted.
16
curl
curl -X PATCH "${API_URL}/api/api-keys/${KEY_ID}/revoke" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.patch(
    f"{API_URL}/api/api-keys/{key_id}/revoke",
    headers={"X-API-Key": API_KEY},
)
print(response.json())
Node.js
const response = await fetch(`${API_URL}/api/api-keys/${keyId}/revoke`, {
  method: "PATCH",
  headers: { "X-API-Key": API_KEY },
});
console.log(await response.json());
17
Delete a revoked key
18
Only revoked keys can be permanently deleted. Active keys must be revoked first.
19
curl
curl -X DELETE "${API_URL}/api/api-keys/${KEY_ID}" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.delete(
    f"{API_URL}/api/api-keys/{key_id}",
    headers={"X-API-Key": API_KEY},
)
print(response.json())  # {"success": true}
Node.js
const response = await fetch(`${API_URL}/api/api-keys/${keyId}`, {
  method: "DELETE",
  headers: { "X-API-Key": API_KEY },
});
console.log(await response.json()); // { success: true }

Best practices

Follow these recommendations to keep your API keys secure and manageable.
  • Principle of least privilege — Grant only the permissions each integration actually needs. A CRM sync that only reads leads should not have leads:import or campaigns:manage.
  • Set expiration dates — Use expiresAt to enforce key rotation on a schedule. Keys without expiration remain active indefinitely.
  • Rotate regularly — Rotate keys at least every 90 days. Use the rotate endpoint for zero-downtime replacement.
  • Use environment separation — Use test keys during development and live keys in production. Never share keys across environments.
  • Never commit keys to source control — Store keys in environment variables or a secrets manager. If a key is accidentally exposed, revoke and rotate it immediately.
  • Monitor usage — Check the lastUsedAt field when listing keys. Keys that have not been used in a long time may be candidates for revocation.
  • Rate limits apply — API keys are subject to rate limiting (500 requests/minute per key, 1000 requests/minute per IP globally).

Required permissions

ActionPermission required
List keysapi_keys:view
Create keyapi_keys:manage
Revoke keyapi_keys:manage
Rotate keyapi_keys:manage
Delete keyapi_keys:manage