# Create an AI label and its rule

Source: https://docs.emelia.io/docs/inbox-api/create-ai-label-rule

Guide: [Inbox API](https://docs.emelia.io/docs/inbox-api.md)

`POST https://api.emelia.io/inbox/api/v1/ai-label-rules`

Creates the AI label and the plain language condition that applies it, in one call. The rule applies to emails synchronised after its creation. Setting `autoReplyEnabled: true` makes the inbox send replies on your behalf. In Emelia, AI features are paid with Emelia credits: each incoming email classified while the workspace has at least one rule, and each auto-draft, is debited from the Emelia account. Setting `autoDraftEnabled` or `autoReplyEnabled` to true never returns `402 PLAN_REQUIRED` in Emelia. Supports `Idempotency-Key`.

## Authentication

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

## Request body

Content type: `application/json`, required.

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | Min length 1. |
| `color` | string | yes | Not validated (any string). |
| `condition` | string | yes | Plain language condition, written like a brief to a colleague. Min length 1. |
| `autoDraftEnabled` | boolean | no | Default: `false`. |
| `autoReplyEnabled` | boolean | no | Makes the inbox send replies on your behalf. Default: `false`. |
| `autoReplyConfidenceThreshold` | integer | no | Default: `85`. Range: 0 to 100. |
| `autoReplyDelayMinutes` | integer | no | Default: `0`. Range: 0 to 15. |
| `autoReplyNotifyOnSend` | boolean | no | Default: `true`. |
| `hideFromInbox` | boolean | no | Default: `false`. |

```json
{
  "name": "Interested",
  "color": "#2563eb",
  "condition": "The prospect shows interest: asks for a call, a demo, pricing or more information.",
  "autoDraftEnabled": true
}
```

## Example request

**cURL**

```bash
curl -X POST "https://api.emelia.io/inbox/api/v1/ai-label-rules" \
  -H "Authorization: $EMELIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Interested",
  "color": "#2563eb",
  "condition": "The prospect shows interest: asks for a call, a demo, pricing or more information.",
  "autoDraftEnabled": true
}'
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/inbox/api/v1/ai-label-rules', {
  method: 'POST',
  headers: { Authorization: process.env.EMELIA_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "name": "Interested",
    "color": "#2563eb",
    "condition": "The prospect shows interest: asks for a call, a demo, pricing or more information.",
    "autoDraftEnabled": true
  }),
});
console.log(await response.json());
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.emelia.io/inbox/api/v1/ai-label-rules",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
    json={
        "name": "Interested",
        "color": "#2563eb",
        "condition": "The prospect shows interest: asks for a call, a demo, pricing or more information.",
        "autoDraftEnabled": True
    },
)
print(response.json())
```

## Responses

### 201: Created.

| Field | Type | Required | Description |
|---|---|---|---|
| `rule` | object | yes |  |
| `rule.id` | string | yes |  |
| `rule.label` | object | yes |  |
| `rule.condition` | string | yes |  |
| `rule.autoDraftEnabled` | boolean | yes |  |
| `rule.autoReplyEnabled` | boolean | yes |  |
| `rule.autoReplyConfidenceThreshold` | integer | yes | Range: 0 to 100. |
| `rule.autoReplyDelayMinutes` | integer | yes | Range: 0 to 15. |
| `rule.autoReplyNotifyOnSend` | boolean | yes |  |
| `rule.hideFromInbox` | boolean | yes |  |
| `rule.createdAt` | string (date-time) | yes |  |
| `rule.updatedAt` | string (date-time) | yes |  |

```json
{
  "rule": {
    "id": "cmrlacme0000000000000001",
    "label": {
      "id": "cmtgacme0000000000000002",
      "name": "Interested",
      "color": "#2563eb",
      "isAiLabel": true
    },
    "condition": "The prospect shows interest: asks for a call, a demo, pricing or more information.",
    "autoDraftEnabled": true,
    "autoReplyEnabled": false,
    "autoReplyConfidenceThreshold": 85,
    "autoReplyDelayMinutes": 0,
    "autoReplyNotifyOnSend": true,
    "hideFromInbox": false,
    "createdAt": "2026-10-01T10:05:00.000Z",
    "updatedAt": "2026-10-02T16:40:00.000Z"
  }
}
```

### 400: `VALIDATION_FAILED`: missing or invalid field, empty condition, threshold or delay out of range.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 401: Missing, malformed, unknown or disabled key. No `X-RateLimit-*` headers.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 402: `PLAN_REQUIRED`: auto-draft or auto-reply on a workspace without a paid plan. Never returned in Emelia.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 403: `WORKSPACE_SUSPENDED`: the Emelia subscription has expired.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 409: `DUPLICATE_RULE` (a label with this name exists) or `CONFLICT` (idempotency).

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 413: `PAYLOAD_TOO_LARGE`: JSON body above 256 KB.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 429: `RATE_LIMIT_EXCEEDED`: per minute limit of the workspace (wait `Retry-After`). `QUOTA_EXCEEDED`: monthly quota exhausted until the 1st of next month UTC (in Emelia the plan cannot be upgraded).

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 500: `INTERNAL_ERROR`: unexpected failure. The message is generic in production.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```
