# Hotline reporting buckets

> What a hotline bucket is, how the hotline picker is populated, and how binding a DID to a hotline affects reporting but not routing.

A **hotline** is a reporting dimension. It groups inbound numbers into a
named bucket so that call volume can be rolled up by campaign, programme,
or line-of-business rather than by individual DID.

On the **Phone numbers** admin screen, the per-number drawer exposes a
**Reporting hotline** field. Setting it writes `phone_number.hotline_id`,
a nullable foreign key to a row in `dim_hotline`. Nothing else on the call
path reads it.

## What a hotline bucket is

A hotline bucket is a row in the `dim_hotline` dimension table, scoped to
one org. The Phone numbers screen reads four of its columns:

| Column   | Use on this screen                                                  |
| -------- | ------------------------------------------------------------------- |
| `id`     | The value stored in `phone_number.hotline_id`.                      |
| `code`   | The short identifier. The picker sorts ascending by this column.    |
| `name`   | The human label shown beside the code.                              |
| `active` | Only `active = true` rows are offered in the picker.                |
| `org_id` | Scopes the list to the currently selected org.                      |

The picker renders each option as `code · name`, so a bucket with code
`SUPPORT-EU` and name `EU support line` appears as
`SUPPORT-EU · EU support line`.

The query behind the field is, in effect:

```sql
select id, code, name
from dim_hotline
where org_id = :orgId
  and active = true
order by code asc;
```

Because `code` drives the sort order, codes with a common prefix
(`SALES-…`, `SUPPORT-…`) group together in the dropdown.

## Creating and deactivating hotline codes

The Phone numbers screen does not create, rename, or deactivate hotline
codes. It is a consumer of `dim_hotline` only — it lists the active rows
and lets you bind one to a number. Codes are maintained outside this
screen, and the drawer will not offer a bucket that does not already
exist for the org.

Two consequences follow from the `active = true` filter:

- **A newly added code appears as soon as it is active.** Reopen the
  drawer (or reload the screen) to refresh the list; the hotline list is
  fetched with the numbers and trunks when the screen loads.
- **Deactivating a code hides it from the picker but does not unbind
  numbers.** Rows that already carry that `hotline_id` keep it. The
  drawer for such a number has no matching active option to display, so
  re-saving the number through the drawer can drop the stale binding.
  Move numbers to a replacement bucket before a code is deactivated.

## Binding numbers to a hotline

Pick a bucket in **Reporting hotline** and save. The field is clearable —
the empty state is `— None —`, which stores `hotline_id: null`.

The save goes through the admin API's phone-number upsert:

```ts
await adminApi.upsertPhoneNumber(orgId, {
  id: initial?.id,
  e164: "+14155550123",
  number_type: "local",
  country: "US",
  city: "San Francisco",
  trunk_id: trunkId || null,
  agent_id: agentId.trim() || null,
  hotline_id: hotlineId || null,   // ← the hotline binding
  provider: "manual",
  status: "active",
});
```

The TeleQuick BFF dual-writes the row to Supabase and to Redis. The
Redis DID index is published or pulled based on `status`, not on
`hotline_id`; changing only the hotline binding does not re-deploy or
undeploy the number.

The drawer's **Deactivate** and **Reactivate** actions pass the existing
`hotline_id` through unchanged. Deactivating a number is a soft delete —
`status` becomes `inactive`, the BFF pulls the Redis DID index so inbound
INVITEs stop resolving, and the row (with its hotline binding) is
retained. Reactivating restores routing with the same bucket still
attached.

`e164` is immutable once a number exists; the hotline binding, trunk, and
agent ID are all editable on an existing number.

## Where hotline roll-ups appear in reports

The binding is a stored dimension key, not a computed field. Reporting
reads `phone_number.hotline_id` and joins `dim_hotline` for the `code`
and `name` labels. Because the key lives on the number and not on the
call, it applies from the moment it is saved — rebinding a number to a
different bucket changes how that number's *subsequent* traffic rolls up
and does not retroactively relabel anything already attributed to the
previous bucket.

If a number has `hotline_id: null`, its traffic carries no hotline
dimension and rolls up only under the org, trunk, and agent dimensions
that the number already has.

## Hotline vs VDN: reporting against routing

These are easy to confuse because both look like "a code attached to a
number". They sit on opposite sides of the call.

| | Hotline (`hotline_id`) | Routing destination |
| --- | --- | --- |
| Purpose | Groups the number for reporting roll-ups | Decides where an inbound call lands |
| Effect on a live call | None | Determines termination |
| Editable here | Yes, **Reporting hotline** | Yes, **Trunk** and **Agent ID** |

The drawer's hint says it plainly: *reports only — no routing effect*.
Changing a hotline never changes who answers.

The destination fields this screen offers are:

- **Trunk** — inbound termination, where matched calls land. Optional;
  the empty state is `— unassigned —`.
- **Agent ID** — optional direct routing to an AI agent, entered as a
  free-text `agent_xxx` identifier.

This screen does **not** expose a `vdn_id` destination. If you are
following provisioning material that describes routing a DID to a VDN,
that destination is not settable from the Phone numbers drawer, and the
**Reporting hotline** field is not a substitute for it — a hotline has no
routing behaviour at all. Set the trunk (and optionally the agent) here
for termination, and use the hotline purely as the reporting label.

## Call rating on these numbers

The drawer states the tariff that applies to calls on the number being
edited: the platform flat tariff of **$0.0005 per call plus $0.0010 per
minute**. This rate is a property of the number, not of the hotline —
selecting or clearing a hotline bucket does not change how calls rate.
Hotline buckets only determine how the resulting usage is grouped in
reports. See Billing for the full rating detail.
