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

# Business Hierarchy

> Organize offers into categories and sub-categories with custom fields, including computed fields with formulas.

## Overview

You organize your offers into a **business hierarchy** -- a two-level structure of **categories** (top-level groupings) and **sub-categories** (nested beneath them). This hierarchy provides logical organization, drives custom field schemas, and enables category-level rules and reporting.

## Categories

A category represents a top-level business domain or product line (e.g., "Credit Cards", "Personal Loans", "Insurance Products").

### Field Reference

| Field          | Type    | Required | Default    | Description                                                               |
| -------------- | ------- | -------- | ---------- | ------------------------------------------------------------------------- |
| `name`         | string  | Yes      | —          | Display name of the category (1–255 chars)                                |
| `description`  | string  | No       | `""`       | Optional description                                                      |
| `icon`         | string  | No       | `""`       | Icon identifier for the UI                                                |
| `color`        | string  | No       | `"blue"`   | Color preset for visual distinction                                       |
| `status`       | enum    | No       | `"active"` | Lifecycle status: `draft`, `active`, `paused`, `archived`                 |
| `ordinal`      | integer | No       | `0`        | Display order position (min 0)                                            |
| `customFields` | array   | No       | `[]`       | Schema definition for fields on offers in this category (up to 500 items) |

### Custom Fields

Categories define a **custom field schema** that applies to all offers within the category. Each custom field has:

| Property     | Type      | Description                                                |
| ------------ | --------- | ---------------------------------------------------------- |
| `name`       | string    | Field identifier (used as key in offer data)               |
| `label`      | string    | Display label in the UI                                    |
| `type`       | enum      | Field type                                                 |
| `required`   | boolean   | Whether the field must be filled                           |
| `options`    | string\[] | Choices for `select` type fields                           |
| `formula`    | string    | Formula expression for `computed` type fields              |
| `outputType` | enum      | Output data type for `computed` fields: `number` or `text` |

### Custom Field Types

| Type       | Description                        | Example                             |
| ---------- | ---------------------------------- | ----------------------------------- |
| `text`     | Free-text string                   | Product description, internal notes |
| `number`   | Integer value                      | Points multiplier, term length      |
| `select`   | Single choice from options         | Risk tier, product variant          |
| `boolean`  | True/false toggle                  | Pre-approved flag, promotional      |
| `date`     | Date value                         | Effective date, review date         |
| `computed` | Formula-evaluated at decision time | Personalized rate, dynamic discount |

<Tip>
  **Computed fields** are the foundation of [dynamic computed values](/tutorials/computed-values). They let you define formulas like `base_rate * (1 - customer.loyalty_score * 0.01)` that are evaluated per customer at decision time.
</Tip>

### Computed Field Formulas

Computed fields use the [formula engine](/tutorials/formula-reference) with three variable namespaces:

| Namespace      | Source                                         | Example                                         |
| -------------- | ---------------------------------------------- | ----------------------------------------------- |
| *(bare name)*  | Other custom field values on the offer         | `base_rate`, `annual_fee`                       |
| `customer.*`   | Enriched data from schema tables               | `customer.loan_amount`, `customer.credit_score` |
| `attributes.*` | Request-time attributes from the Recommend API | `attributes.tier`, `attributes.channel`         |

#### Formula Examples

**1. Personalized interest rate** — discount based on loyalty score:

```
Name: personalized_rate
Formula: base_rate * (1 - customer.loyalty_score * 0.01)
Output Type: number
```

If `base_rate` is 12.5 and `customer.loyalty_score` is 80, the result is `12.5 * (1 - 0.8) = 2.5`.

**2. Dynamic credit limit** — capped at 3x income with a floor:

```
Name: credit_limit
Formula: max(min(customer.annual_income * 3, 50000), 5000)
Output Type: number
```

Uses the `min` and `max` functions to ensure the limit stays between 5,000 and 50,000.

**3. Tiered welcome bonus** — conditional on customer tier:

```
Name: welcome_bonus
Formula: customer.credit_score >= 750 ? 500 : customer.credit_score >= 650 ? 250 : 100
Output Type: number
```

Uses nested ternary expressions: customers with excellent credit get 500, good credit get 250, and everyone else gets 100.

**4. Formatted offer label** — concatenated string:

```
Name: offer_label
Formula: concat(attributes.channel, " - ", coalesce(customer.preferred_name, "Customer"))
Output Type: text
```

Uses `concat` to build a string and `coalesce` to fall back if the preferred name is null.

<Info>
  See the [Formula Reference](/tutorials/formula-reference) for the complete list of supported operators and functions (min, max, round, abs, coalesce, concat, ternary). In the [Composable Pipeline](/data/transforms/composable-pipeline), the Compute node also evaluates these formulas.
</Info>

### Computed Field Validation

When you create or update a category with computed fields, KaireonAI validates:

1. **Formula is present** — a computed field must have a non-empty `formula` string.
2. **Output type is valid** — `outputType` must be `"number"` or `"text"`.
3. **Syntax check** — click **Validate** in the UI to parse the formula and catch syntax errors before saving.

## Sub-Categories

Sub-categories provide a second level of grouping beneath a category (e.g., "Premium Cards" and "Travel Cards" under "Credit Cards").

### Sub-Category Fields

| Field         | Type   | Description                              |
| ------------- | ------ | ---------------------------------------- |
| `name`        | string | Display name                             |
| `description` | string | Optional description                     |
| `categoryId`  | string | Parent category ID                       |
| `ordinal`     | number | Display order within the parent category |

With your hierarchy in place, you can create offers within it. Before doing so, understand how deletions cascade through the hierarchy.

## Cascade Behavior

<Warning>
  **Deleting a category** will cascade-delete all its sub-categories. However, offers are **unlinked** (their `categoryId` and `subCategoryId` are set to null) rather than deleted, preserving historical data and interaction history.
</Warning>

**Deleting a sub-category** unlinks its offers in the same way — offers are preserved but lose their sub-category association.

Here is a summary of what happens at each level:

| Action                | Sub-Categories  | Offers                                 | Creatives                                     | Decision Traces                         |
| --------------------- | --------------- | -------------------------------------- | --------------------------------------------- | --------------------------------------- |
| Delete a Category     | Cascade-deleted | Unlinked (`categoryId` set to null)    | Unaffected (linked to offers, not categories) | Preserved with original category ID     |
| Delete a Sub-Category | N/A             | Unlinked (`subCategoryId` set to null) | Unaffected                                    | Preserved with original sub-category ID |

<Note>
  Because offers are unlinked rather than deleted, you will not lose any historical decision traces, outcomes, or experiment data when reorganizing your hierarchy.
</Note>

## Creating a Category

<Steps>
  <Step title="Navigate to Business Hierarchy">
    Go to **Studio > Business Hierarchy** in the sidebar.
  </Step>

  <Step title="Click Create Category">
    Click the **+ New Category** button.
  </Step>

  <Step title="Fill in category details">
    Enter the name, description, icon, and color.
  </Step>

  <Step title="Define custom fields">
    Add custom fields that will apply to all offers in this category. For each field, specify the name, label, type, and whether it is required.
  </Step>

  <Step title="Add computed fields (optional)">
    For computed fields, enter the formula expression and select the output type (`number` or `text`). Click **Validate** to check the formula syntax before saving.
  </Step>

  <Step title="Save">
    Save the category. It is immediately available for creating offers and sub-categories.
  </Step>
</Steps>

## Creating a Sub-Category

<Steps>
  <Step title="Select parent category">
    In the Business Hierarchy view, click on the category you want to add a sub-category to.
  </Step>

  <Step title="Click Add Sub-Category">
    Click the **+ Sub-Category** button within the category detail panel.
  </Step>

  <Step title="Fill in details">
    Enter the sub-category name and optional description.
  </Step>

  <Step title="Save">
    Save the sub-category. It appears nested under its parent category.
  </Step>
</Steps>

## API Reference

### Create a Category

```bash theme={null}
POST /api/v1/categories
Content-Type: application/json
```

**Request body:**

```json theme={null}
{
  "name": "Personal Loans",
  "description": "Personal lending products",
  "icon": "banknotes",
  "color": "blue",
  "status": "active",
  "ordinal": 2,
  "customFields": [
    { "name": "base_rate", "label": "Base APR", "type": "number", "required": true },
    { "name": "term_months", "label": "Term (Months)", "type": "number", "required": true },
    { "name": "min_credit_score", "label": "Min Credit Score", "type": "number", "required": false },
    {
      "name": "personalized_rate",
      "label": "Personalized Rate",
      "type": "computed",
      "formula": "base_rate * (1 - customer.loyalty_score * 0.01)",
      "outputType": "number"
    },
    {
      "name": "credit_limit",
      "label": "Dynamic Credit Limit",
      "type": "computed",
      "formula": "max(min(customer.annual_income * 3, 50000), 5000)",
      "outputType": "number"
    }
  ]
}
```

**Response (201 Created):**

```json theme={null}
{
  "id": "cat_personal_loans",
  "name": "Personal Loans",
  "description": "Personal lending products",
  "icon": "banknotes",
  "color": "blue",
  "status": "active",
  "ordinal": 2,
  "customFields": [ ... ],
  "createdAt": "2026-03-10T14:30:00Z",
  "updatedAt": "2026-03-10T14:30:00Z"
}
```

### Create a Sub-Category

```bash theme={null}
POST /api/v1/sub-categories
Content-Type: application/json
```

Pass the parent `categoryId` in the request body:

```json theme={null}
{
  "categoryId": "cat_personal_loans",
  "name": "Debt Consolidation",
  "description": "Loans for consolidating existing debt",
  "ordinal": 1
}
```

### List Categories

```bash theme={null}
GET /api/v1/categories
```

Returns all categories with their sub-categories and custom field schemas.

### Delete a Category

```bash theme={null}
DELETE /api/v1/categories?id=<categoryId>
```

The `id` can be either a UUID or the category name (the API resolves names automatically).

<Warning>
  This cascade-deletes all sub-categories and unlinks all offers. This action cannot be undone.
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Offers" icon="tag" href="/studio/offers">
    Create offers within your categories.
  </Card>

  <Card title="Computed Values" icon="calculator" href="/tutorials/computed-values">
    Full guide to dynamic computed values and formula syntax.
  </Card>

  <Card title="Formula Reference" icon="function" href="/tutorials/formula-reference">
    Complete operator and function reference for the formula engine.
  </Card>

  <Card title="Composable Pipeline" icon="cubes" href="/data/transforms/composable-pipeline">
    Use the Compute node to evaluate formulas at decision time.
  </Card>
</CardGroup>
