# The console (plan AF slice A6, AF-6)

`/console` on the API host, the Service's alone (`ConsoleDoor`). It follows the approved mockups in `reji/ui/html/af/`. `resources/css/console.css` carries their look, with its tokens mapped onto the app's.
On screen an organisation is a client (the Architect, 6 November 2026); in code it stays `tenant`, and at `id` it stays an organisation.

| Page | Route name | Controller | Reads |
|---|---|---|---|
| Dashboard | `console.dashboard` | `Console\DashboardController` | `ServerBoard` |
| Clients, a record, adding and editing, granting | `console.client.*` | `Console\ClientController` | `ClientHoldings`, the one read across organisations |
| Usage, limits | `console.usage.*` | `Console\UsageController` | `UsageReport` |
| Messages, one message | `console.message.*` | `Console\MessageController` | The `message` log (`notify.md`) |
| Vouchers, a campaign, a code (plan TE, TE3; mockups `te/campaign.html`, `te/code.html`; the list follows `tl/links.html`) | `console.voucher.index`, `.find`, `.campaign`, `.code`, `.code.void`, `.batch.void`, `.campaign.close`, `.batch.file` (behind `fresh`) | `Console\VoucherController` | `voucher_campaign`, `voucher`, `voucher_batch`, `voucher_line`; reasons from Input Fields `voucher_void_reason`; `vouchers.changed` to the product |
| Links, one link (plan TL, TL3; mockup `tl/links.html`) | `console.link.index`, `.show`, `.off` | `Console\LinkController` | `link` and its taps by day; reasons from Input Fields `link_off_reason` |
| Who isn't written to, one address (plan NU, NU6; mockup `nu/blocked.html`) | `console.message.blocked.index`, `.show`, `.store`, `.lift` | `Console\BlockController` | `send_block` and its lines; lift reasons from Input Fields `block_lift_reason` |
| Products' e-mail, one e-mail (plan NU, NU5) | `console.message.email.index`, `.show` | `Console\ProductEmailController` | The chain with each mailer's month against its limit, and `product_email` |
| Sender names (plan NU, NU4) | `console.message.sender.index`, `.update`, `.company` | `Console\SenderController` | `SenderNames::summary` for each client with a name; the company's name with each provider |
| Bulk sends, one send (plan NU, NU3; mockup `nu/batch.html`) | `console.message.batch.index`, `.show`, `.cancel` | `Console\BatchController` | `message_batch` and its texts' counts; the page refreshes every 3 seconds while a send goes |
| Channels, connecting a provider | `console.channel.*` | `Console\ChannelController` | `ChannelReport` |
| Billing: offerings, agreed prices, an organisation's own billing, switching billing on (plan TB) | `console.billing.*` | `Console\BillingController`, `OfferingController`, `AgreedPriceController`, `BillingLaunchController` | The catalogue, payments and periods across organisations (`subscription.md`) |
| Withholding certificates (plan TB §8, TB10) | `console.billing.withholding`, `console.billing.withholding.*` | `Console\WithholdingController` | `Withholdings`, withholdings across organisations |
| SMS credit packs (plan NU, NU2) | `console.billing.texts`, `.texts.store`, `.texts.update` | `Console\SmsPackController` | `sms_pack`, the texts bought this month |
| Money back: refunds, those made in Paystack's dashboard, payments set aside (plan TB §7, TB7), and chargebacks (TB9) | `console.billing.money_back`, `console.billing.refund.*`, `console.billing.dispute.answer` | `Console\MoneyBackController` | `Refunds`, refunds and payments across organisations |

| Action | Permission |
|---|---|
| Reading the dashboard, usage, messages and channels, checking a server | `operations.view` |
| Reading clients | `workspace.view` |
| Adding and editing clients, granting, limits, sending a text again, stopping a bulk send, recording a provider's answer on a sender name and the company's own, blocking an address by hand and lifting a block, turning a short link off, the short-link setting, voiding a voucher code or batch, closing a campaign, printing a batch (a fresh proof too) | `workspace.create` |
| Connecting a provider or changing its key, switching which sends | `workspace.create` and the password |
| Connecting a server, issuing a key | `workspace.create` and the password again |
| Reading Billing and Money back | `offering.view` |
| Changing offerings, agreed prices, an organisation's billing, switching billing on | `offering.manage` and the password again |
| Accepting or rejecting a withholding certificate | `certificate.review` and the password again |
| Giving money back, choosing what a refund made in Paystack does, recording a payout by hand, asking Paystack again, answering a chargeback | `refund.manage` and the password again |

Suspending and resuming use the starter pack's `platform.workspace.status`, whose page, `/console/workspace`, stays but is off the menu. Its "make a workspace and invite who runs it" flow has no place on the console's screens until accounts (plan CL) decide it.

The menu puts the console's pages first, then the starter pack's, with its logs and lists folded into Records (`Nav::ITEMS`). The Service with no lens has no Home: `/` sends it to the dashboard.

A drawer (a server's, a payment's, a refund's) sits over the shell and under anything that opens over it: a picker's list, a dialog (the password again included) and the idle warning (`console.css`).

Issuing a key in a server's drawer rotates: with two already live it revokes the older first, since a server holds two at most.

**Keys and access** (plan SEC, SEC4 F3, F4), in every connected server's drawer: the modules its keys may call, as chips (chosen at connecting too; a change reaches its live keys at once, `PUT /console/server/{server}/modules`); each live key by its last four, when made and last used and the day it ends (a year by default, `ServerKeys::LIFE_MONTHS`; readiness's `server_keys` warns a month before), with **Revoke this key** (`DELETE …/key/{key}`); and **Switch this server off** (`POST …/off`): every key, its sign-in client and every token it gave out. Each with the password again. For an incident without the console: `php artisan server:revoke {server} --key=<last four>` or `--off`.

A product icon is `public/products/<code>.svg`, from the mockups. Not branding: a picture of each product on staff's own screen.
