API tokens authenticate requests without user credentials. Each `lum_` token is scoped to one organization and to a **permission set**. OAuth CLI sessions stay full-access operators. Existing tokens created before permissions shipped keep full access (`*`).

## Creating an API Token

### From the CLI (recommended for agents)

After `lumail auth login`, mint a durable token for an app or CI:

```bash
lumail tokens create --name "In-app integration"
```

Defaults to the **App** preset: `subscribers`, `emails`, `audience`. That is
enough for SDK upserts and SMTP. It cannot send campaigns or mint tokens.

```bash
lumail tokens create --name "In-app integration" --full
lumail tokens create --name sender --preset sender
lumail tokens create --name custom --permissions subscribers,audience
```

Reuse only works when the same name **and** the same permission set already
exist. A full token named `In-app integration` will not be returned for an App
create — that is `409` `token_permission_mismatch`. Use `--no-reuse` or a
different name. The `lum_` secret is printed once. `lumail tokens list` shows
names, last-4, and permissions.

### Step 1: Navigate to API Tokens Page

1. Log in to your Lumail account
2. Select your organization from the sidebar
3. Go to **Settings** → **API Tokens**
4. Click the **Generate Token** button

### Step 2: Name Your Token and pick permissions

Give your token a descriptive name and a permission preset. The dashboard
defaults to **App**. Full access is an explicit choice. Permissions cannot be
edited later — rotate the token instead.

Presets:

- **App** — subscribers, emails, audience
- **Marketing editor** — App plus campaigns, analytics, workflows
- **Sender** — marketing editor plus campaign send/schedule
- **Full** — everything, including tokens and domains
- **Custom** — any set of groups

A missing group returns **403** with `code: "missing_permission"` and
`required` set to the group (not a 404).

Give your token a descriptive name to help you identify its purpose later:

- `Production API` - For production applications
- `Development` - For local development
- `Integration Testing` - For CI/CD pipelines
- `Mobile App` - For mobile applications

### Step 3: Save Your Token

**IMPORTANT:** After creation, your token will be displayed only once. Copy and store it securely immediately.

```
lum_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
```

The token format is: `lum_` followed by a 64-character random string.

## Using Your API Token

### Authentication Header

All API requests must include the token in the `Authorization` header using the Bearer authentication scheme:

```
Authorization: Bearer lum_your_token_here
```

### Basic Request Structure

Every API request should include:

1. **Authorization Header** - Your API token with Bearer prefix
2. **Content-Type Header** - Usually `application/json`
3. **Request Body** - For POST/PUT/PATCH requests (JSON format)

## Usage Examples

### Example 1: Sending a Transactional Email

```javascript
const response = await fetch("https://lumail.io/api/v1/emails", {
  method: "POST",
  headers: {
    Authorization: "Bearer lum_your_token_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "user@example.com",
    subject: "Welcome!",
    content: "Hello {{name}}, welcome to our platform!",
    from: "hello@yourdomain.com",
  }),
});

const data = await response.json();
console.log(data);
```

### Example 2: Creating a Subscriber

```javascript
const response = await fetch("https://lumail.io/api/v1/subscribers", {
  method: "POST",
  headers: {
    Authorization: "Bearer lum_your_token_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "newuser@example.com",
    name: "Jane Smith",
    tags: ["newsletter", "promotional"],
  }),
});

const data = await response.json();
console.log(data);
```

### Example 3: Listing All Tags

```javascript
const response = await fetch("https://lumail.io/api/v1/tags", {
  method: "GET",
  headers: {
    Authorization: "Bearer lum_your_token_here",
    "Content-Type": "application/json",
  },
});

const data = await response.json();
console.log(data.tags);
```

### Example 4: Error Handling

Always implement proper error handling for API requests:

```javascript
try {
  const response = await fetch("https://lumail.io/api/v1/subscribers/sub_123", {
    method: "GET",
    headers: {
      Authorization: "Bearer lum_your_token_here",
      "Content-Type": "application/json",
    },
  });

  if (!response.ok) {
    const error = await response.json();
    console.error("API Error:", error.message);
    return;
  }

  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error("Network Error:", error);
}
```

## Best Practices

### Security

1. **Never commit tokens to version control** - Use environment variables
2. **Store tokens securely** - Use secret management services
3. **Rotate tokens regularly** - Generate new tokens periodically
4. **Use separate tokens** - Different tokens for dev, staging, and production
5. **Delete unused tokens** - Remove tokens you're no longer using

### Environment Variables

Store your API token in environment variables:

```bash
# .env.local (DO NOT commit this file)
LUMAIL_API_TOKEN=lum_your_token_here
```

Then use it in your code:

```javascript
const response = await fetch("https://lumail.io/api/v1/subscribers", {
  headers: {
    Authorization: `Bearer ${process.env.LUMAIL_API_TOKEN}`,
    "Content-Type": "application/json",
  },
});
```

### Rate Limiting

API requests are rate-limited based on your subscription plan. See [Rate Limits](/docs/api-reference/api-limits) for full details. Implement exponential backoff for failed requests:

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.ok) {
      return response;
    }

    if (response.status === 429) {
      // Rate limited - wait and retry
      const waitTime = Math.pow(2, i) * 1000;
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      continue;
    }

    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  }

  throw new Error("Max retries exceeded");
}
```

## Common Errors

### 401 Unauthorized - Missing Authorization Header

```json
{
  "message": "Missing or invalid Authorization header"
}
```

**Solution:** Ensure you're including the `Authorization: Bearer <token>` header in your request.

### 401 Unauthorized - Invalid API Token

```json
{
  "message": "Invalid API token"
}
```

**Solution:**

- Verify your token is correct (no extra spaces or characters)
- Check if the token has been deleted from the settings page
- Generate a new token if necessary

### 404 Not Found - Resource Not Found

```json
{
  "error": "Subscriber not found"
}
```

**Solution:** Verify the resource ID exists in your organization.

### 403 Forbidden - Missing Permission

```json
{
  "message": "Missing permission: send",
  "code": "missing_permission",
  "required": "send"
}
```

**Solution:** Mint a token that includes the required group (`--preset sender` / `--full`, or a custom `--permissions` list). Permissions cannot be edited — rotate the token.

The SDK throws `LumailPermissionError` with `required`. The CLI prints the group and a mint hint.

### 400 Bad Request - Validation Error

```json
{
  "message": "Validation failed",
  "errors": [
    {
      "field": "email",
      "message": "Invalid email format"
    }
  ]
}
```

**Solution:** Check your request body matches the expected schema for the endpoint.

## Managing Tokens

### Viewing Active Tokens

Go to **Settings** → **API Tokens** to see all active tokens for your organization:

- Token name
- Creation date
- Last used date (if applicable)
- Last 7 days of actions (REST, v2 tools, MCP) with success / warning / error messages. Rows older than 7 days are deleted automatically. The raw `lum_` secret is never stored on a log row.

### Deleting Tokens

To delete a token:

1. Navigate to **Settings** → **API Tokens**
2. Find the token you want to delete
3. Click the delete icon
4. Confirm the deletion

**Note:** Deleting a token immediately revokes access. Any applications using that token will fail authentication.

### Token Rotation

For security best practices, rotate your tokens periodically:

1. Generate a new token
2. Update your applications to use the new token
3. Test that the new token works
4. Delete the old token

## Organization Scope

Each API token is scoped to a specific organization. The token provides access to:

- All subscribers in your organization
- All campaigns and emails
- All tags and custom fields
- All analytics and reports
- Organization settings (where applicable)

Tokens **cannot** access:

- Other organizations' data
- User account settings
- Billing information beyond basic plan details

## Related Documentation

Now that you have your API token set up, explore the available API endpoints:

- [Send Transactional Emails](/docs/api-reference/api-emails-send) - Send emails via API
- [Create Subscribers](/docs/api-reference/api-subscribers-post) - Add new subscribers
- [Manage Subscribers](/docs/api-reference/api-subscribers-get) - Retrieve subscriber data
- [Work with Tags](/docs/api-reference/api-tags-get) - Manage subscriber tags
- [Track Events](/docs/api-reference/api-events-post) - Record subscriber activity

### Tutorials

- [Create API Token Tutorial](/docs/tutorials/create-api-token) - Step-by-step token setup
- [V0 Capture Page](/docs/tutorials/v0-capture-page) - Build capture forms with API
- [Dynamic Promo Codes](/docs/tutorials/dynamic-promo-codes) - Use tokens in webhooks
