# Apps & keys

> Create realtime apps, read the one-time secret, rotate credentials, and tell app keys apart from voice agent keys.

An **app** is the credential boundary for realtime traffic. Each app carries
an app ID, a key, and a secret. Your servers sign HTTP API calls and channel
auth responses with the secret. Clients connect with the key. Apps are scoped
to an organization, so the Apps & keys screen requires a signed-in session
with an org selected — until then it shows a sign-in prompt and does not call
the app list procedure at all.

The key/secret pair is Pusher-compatible. A stock `pusher-js` client pointed
at an app's key connects without a custom transport.

## What an app is: app ID, key, secret

The app table lists one row per app with these columns:

| Column   | Value                                                                 |
| -------- | --------------------------------------------------------------------- |
| App      | The app name, plus `app_id <…>` underneath it.                         |
| Key      | The full client-side key. Safe to ship to clients.                     |
| Secret   | A masked tail only (`secretMasked`). Never the full value.             |
| Status   | `active`, or the raw status value when the app is not active.          |
| Tags     | Editable org tags for this app.                                        |

Rows whose status is not `active` render dimmed, with the status string shown
in place of the `active` pill.

The three identifiers do different jobs:

- **App ID** identifies the app in HTTP API paths and in webhook payloads.
- **Key** is the public half. It goes in client config.
- **Secret** is the private half. It stays on your servers. ClutchCall
  seals it at rest and the API does not return it after it has been issued.

## The one-time reveal and the masked tail

When an app is created or a secret is rotated, the screen renders a
one-time reveal panel above the table. It is the only place the full secret
ever appears.

- On **create**, the panel shows three fields: App ID, Key, and
  `Secret (shown once)`.
- On **rotate**, the panel shows one field: `New secret (shown once)`.

Dismissing the panel — via the close button or **I've stored it** — discards
it from the UI. There is no way to reopen it. Reloading the screen will not
bring it back, because the list procedure returns only the masked tail.

If you lose a secret, the recovery path is rotation, not retrieval. Rotating
issues a fresh secret and shows it once, under the same rules.

Copy the secret into your secret manager before you dismiss the panel.

## Creating an app

Press **Create app** to open the create card, then fill in **App name** and
press **Generate**. The key and secret are generated server-side; you do not
supply them.

Naming rules enforced by the form:

- The name is required. An empty or whitespace-only name blocks submission.
- The name is capped at 80 characters.

Validation errors stay hidden until you first press **Generate**, then appear
inline under the field. The name is trimmed before it is sent.

If creation fails, the error message from the server is shown inline at the
bottom of the create card and the card stays open with your input intact.

When there are no apps yet, the screen shows an empty state with its own
**Create app** button instead of the table; the header button appears once at
least one app exists.

## Rotating a secret without dropping traffic

**Rotate secret** lives in the row's overflow menu (the `⋯` button). It asks
for confirmation first, and the dialog states the consequence plainly: the new
secret is shown once, and every server still signing with the current secret
stops authenticating immediately.

There is no overlap window. The old secret is invalid the moment the new one
is issued. Plan the rollout accordingly:

1. Make sure your servers read the secret from a source you can update
   without a code deploy — an environment variable backed by a secret
   manager, or a config store your processes re-read.
2. Rotate in the console and copy the new secret from the reveal panel.
3. Write it to that source and roll your signing servers.

Any request signed with the previous secret and still in flight during that
window fails authentication rather than being honoured. Client connections
authenticate with the key, not the secret, so they are not the thing at risk —
the exposure is on your server-side signing path, including private and
presence channel auth responses.

Rotation does not change the app ID or the key. Clients do not need new
config.

## Renaming an app

The name is editable in place, from the same screen that displays it. Click
the pencil next to the name, or choose **Rename** from the row menu.

- **Enter** commits the change; **Escape** cancels.
- The check button commits; the × cancels.
- An empty name, or one longer than 80 characters, is not committed.
- Committing the same name the app already has just closes the editor without
  a write.

A failed rename surfaces as an inline note above the table. The app's key,
secret, and app ID are untouched by a rename.

## Deleting an app

**Delete app** is the destructive entry at the bottom of the row menu and
requires confirmation. The dialog spells out the blast radius: all keys,
channels, and webhooks belonging to the app are removed, and connected
clients are dropped immediately.

The app switcher only clears its selection after the server confirms the
delete. If the delete is rejected, the selection stays as it was and the
failure is reported inline.

## Voice agent keys shown on this screen

Below the app table, the screen lists **Voice agent keys** — read-only
credentials for externally-hosted voice agents. This block only renders when
the org has at least one such credential.

| Column   | Value                                                        |
| -------- | ------------------------------------------------------------ |
| Agent    | The agent name, with its `handle` underneath.                 |
| Key      | The full key.                                                 |
| Secret   | A masked tail only.                                           |
| Status   | `active`, or the raw status when not active.                  |
| Endpoint | The agent's media path.                                       |

These are **not interchangeable with an app key**. They come from a different
table and a different engine registry, and they authenticate the `/media/`
transport only — not channel connections and not the realtime HTTP API.
Signing a realtime API request with a voice agent secret does not
authenticate, and vice versa.

They appear here because an operator auditing "which credentials does this org
have in the wild" needs to see both sets. Nothing on this screen mutates them:
create, rotate, and delete for voice agent credentials live in
**Voice AI → External agents**.

## Tagging and filtering apps

Each app row has a tag cell. Tags are org-scoped and stored against the
resource kind `realtime_app`, keyed by the app's internal id. Edit them in
place from the cell.

The tag filter in the page header narrows the table to apps matching the
filter and reports how many of the total rows matched. Filtering is applied to
the table only — it does not affect the voice agent keys block.

## Related

- [Authentication](/concepts/authentication) — how server credentials relate
  to short-lived client tokens
- [Telemetry](/platform/telemetry) — operational data emitted per tenant
