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

# Quote Engine

> Turn GENERATE_QUOTE into a real quoting engine — the agent picks line items, the server computes every amount

The **Quote Engine** upgrades the `GENERATE_QUOTE` tool from a simple "request a quote" relay into a tool that **computes and sends a real, numbered quote** during the conversation — with line items, discounts, VAT and a total.

The key principle: **prices never come from the LLM.** The agent only chooses *references* (a `sku` and a quantity); every amount is read from a pricing grid you control, server-side. A hallucinated price is impossible by design.

## Two modes

`GENERATE_QUOTE` has two behaviors, decided automatically by its config:

| Mode       | Active when                      | Behavior                                                                          |
| ---------- | -------------------------------- | --------------------------------------------------------------------------------- |
| **Engine** | `config.pricing` is a valid grid | Agent picks `sku` + `qty`, server computes the quote, sends a numbered email      |
| **Legacy** | no `pricing`                     | Agent collects the request, two notification emails are sent (no computed amount) |

Adding a `pricing` grid to an existing `GENERATE_QUOTE` tool switches it to engine mode. Removing it reverts to legacy. Existing tools are unaffected until you add a grid.

## The pricing grid

The grid lives in `config.pricing`. **All amounts are in cents** (an integer), so `150000` = €1,500.00.

```json theme={null}
{
  "currency": "EUR",
  "items": [
    { "sku": "site-vitrine", "label": "5-page website",        "unit": 150000, "per": "flat" },
    { "sku": "page-supp",    "label": "Extra page",            "unit": 12000,  "per": "unit", "max": 20 },
    { "sku": "maintenance",  "label": "Monthly maintenance",   "unit": 8000,   "per": "month" }
  ],
  "rules": [
    { "whenTotalGte": 300000, "discountPct": 5, "label": "Volume discount" }
  ],
  "vat": 20,
  "validityDays": 30,
  "disclaimer": "Indicative estimate, not contractually binding."
}
```

### Fields

| Field                  | Required | Description                                                        |
| ---------------------- | -------- | ------------------------------------------------------------------ |
| `currency`             | no       | Currency code. Defaults to `EUR`.                                  |
| `items[]`              | **yes**  | The catalogue. At least one item.                                  |
| `items[].sku`          | **yes**  | Stable reference the agent uses. Must be unique.                   |
| `items[].label`        | **yes**  | Human label shown on the quote.                                    |
| `items[].unit`         | **yes**  | Unit price **in cents** (positive integer).                        |
| `items[].per`          | no       | Unit wording (`flat`, `unit`, `month`, `hour`…). Display only.     |
| `items[].max`          | no       | Maximum quantity allowed for this line.                            |
| `rules[]`              | no       | Threshold discounts.                                               |
| `rules[].whenTotalGte` | —        | Apply when the pre-discount subtotal (cents) reaches this value.   |
| `rules[].discountPct`  | —        | Discount percentage, in `]0, 100]`.                                |
| `vat`                  | no       | VAT rate in percent. Omit or set `null` for HT-only (no tax line). |
| `validityDays`         | no       | Quote validity, shown on the email.                                |
| `disclaimer`           | no       | Footer text. Defaults to an "indicative estimate" notice.          |

<Note>
  Discount rules are **structured** (`whenTotalGte` / `discountPct`), not free-text expressions. Animam never evaluates a tenant-supplied formula. When several rules qualify, the one with the highest discount wins.
</Note>

## How the agent uses it

In engine mode the tool exposes a single structured input:

```json theme={null}
{
  "visitorName": "Jane Doe",
  "visitorEmail": "jane@example.com",
  "lines": [
    { "sku": "site-vitrine", "qty": 1 },
    { "sku": "page-supp",    "qty": 4 }
  ],
  "note": "Optional cover note — text only, never an amount",
  "consentConfirmed": true
}
```

* `sku` is an **enum** of your catalogue references — the agent cannot invent one.
* `qty` is an integer; the server enforces any `max`.
* `note` is free text the agent may write (a cover line). It is escaped and rendered as-is; it never carries pricing.
* The agent never sends a price. The server computes `subtotal → discount → VAT → total`.

### Soft-fail loop

If the agent references an unknown `sku` or a quantity above `max`, the tool returns a soft failure with a clear message, and the agent asks the visitor to clarify before retrying. Nothing is sent.

### Consent

Engine mode enforces explicit consent (GDPR): the tool refuses unless `consentConfirmed: true`, which the agent only sets after the visitor explicitly agrees to receive the quote by email. This guard applies to engine mode only — legacy `GENERATE_QUOTE` is unchanged.

## The output

On success the engine:

1. Computes the quote deterministically from the grid.
2. Assigns a number `DEVIS-YYYY-NNN` (per-tenant, per-year sequence).
3. Sends an HTML email with the line items, subtotal, discount, VAT and total — to the visitor and a copy to the tenant.
4. Persists a `ToolSubmission` with `quoteNumber`, `lines` and `totals`.
5. Returns the number and total to the agent, which announces them to the visitor.

Amounts in the email are rendered from the server computation; the LLM has no way to alter them.

## Creating an engine-mode tool

### Via the dashboard

Go to **Tools → Quote request → Computed quote** and paste the pricing grid as JSON. The grid is validated when you save (unique skus, positive integer units, discount bounds, VAT range). An invalid grid is rejected with the reason.

### Via the API

```bash theme={null}
curl -X POST https://api.animam.ai/tenants/your-slug/tools \
  -H "Authorization: Bearer ak_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "GENERATE_QUOTE",
    "name": "generate_quote",
    "description": "Build a priced quote from the catalogue and email it to the prospect.",
    "config": {
      "pricing": {
        "currency": "EUR",
        "items": [
          { "sku": "site-vitrine", "label": "5-page website", "unit": 150000, "per": "flat" },
          { "sku": "page-supp", "label": "Extra page", "unit": 12000, "per": "unit", "max": 20 }
        ],
        "rules": [{ "whenTotalGte": 300000, "discountPct": 5, "label": "Volume discount" }],
        "vat": 20,
        "validityDays": 30
      }
    }
  }'
```

<Note>
  Quote tools are included from the **Builder** plan. On **Starter**, they are available as a €9/mo add-on. The pricing grid lives in `Tool.config` — no database migration is involved.
</Note>

## Security model

* **Amounts are server-authoritative.** The LLM supplies `sku` + `qty` only; prices are read from the grid. This is the same rule as the `VERIFIED_ACTION` tool.
* **No tenant code is evaluated.** Discounts are structured data, never an expression.
* **Consent is enforced** before any email is sent.
* **Email values are HTML-escaped**, including the visitor name and the agent's cover note.

## Roadmap (v2)

* PDF rendering of the quote.
* Quote statuses (sent / accepted) and follow-up.
* A dedicated grid editor UI (today the dashboard uses JSON editing).
* A "pay the deposit" link, coupling with `COLLECT_PAYMENT`.
