App bars are the UI an installed app adds to Caraer. A button on a record,
a dialog that collects a file, a tab next to traits, or a tool in the sidebar
— those are all app bars.

Define them in `app.caraer.yaml` (`appBars`). There is no
`caraer apps add app-bar` scaffold. Push deploys the bar; the **installer**
chooses where it is visible (which objects, or which suites for the sidebar
tool).

If you have not scaffolded an app yet, start with
[How to create a Caraer app](/blog/2026-08-23-how-to-create-a-caraer-app).

## Two kinds

| Kind | Locations | What the user sees | What you ship |
|  --- | --- | --- | --- |
| **Action** | `RECORD_PREVIEW`, `RECORD_OVERVIEW`, `RECORD_TRAIT` | A button. Click opens a dialog, then a webhook runs | `webhook` + optional `settingsSchema`. No `iframeUrl` |
| **Iframe** | `RECORD_DETAIL`, `TOOL_BAR`, `TRAIT_BAR` | An embedded page | `iframeUrl` (required). Webhook is optional |


Action bars must not set `iframeUrl`. Iframe bars must set it. The platform
rejects the other combination.

## Where they appear

| `location` | In the Caraer app | Context passed through |
|  --- | --- | --- |
| `RECORD_PREVIEW` | Action buttons under the record preview card | Current record |
| `RECORD_OVERVIEW` | Action buttons in the object view header (list / board) | Current object, view, and trait |
| `RECORD_TRAIT` | Action buttons on a trait overview (page cards now; other traits later) | Current record, object, view, and trait |
| `RECORD_DETAIL` | Embedded iframe on the record detail page | Current record |
| `TOOL_BAR` | Tool entry in the left sidebar, with the other tools | Company (no record) |
| `TRAIT_BAR` | Extra tab next to the record's traits | Current view and trait |


Preview and overview are the usual place for “do something to this record”
(parse a CV, sync now). Detail and trait bar are for a persistent UI on the
record. The tool bar is a company-wide surface — calendars, inboxes, dashboards.

## Who sees a bar

Declaring `appBars` on the app is not enough. After install, visibility lives
on the `HAS_APP` edge (`appBarVisibility`), keyed by app-bar UUID:

- **Record locations** (`RECORD_PREVIEW`, `RECORD_OVERVIEW`, `RECORD_DETAIL`,
and `TRAIT_BAR`) — list of **object names**. The bar shows only on those
objects. An empty list means hidden.
- **`RECORD_TRAIT`** — **object names** and **trait names**. Both lists must
match the current object and trait. Installers can currently pick **page**;
other traits will be added when those overviews have a mount. An empty
traits list hides the bar.
- **`TOOL_BAR`** — list of **suites**. The bar shows when the company has one
of those suites. An empty list means hidden.


Installers set this in **Settings → Apps** on the installation. Authors do
not hard-code object names in the manifest for placement.

## Action bars

Give the bar a webhook. Topic is always `app.bar.triggered` — not
`record.{object}.created`. Delivery is `SERVERLESS` (a function in your app)
or `HTTP`.

`settingsSchema` on the **bar** (not the app) is the dialog the user fills
before the webhook runs. `FILE` and `MULTI_FILE` belong here, not on
installation settings.


```yaml
appBars:
  - name: upload_cv
    location: RECORD_OVERVIEW
    label: Upload CV
    actionLabel: Parse CV
    tooltipLabel: Parse a CV onto this candidate
    description: Upload a CV. Caraer will parse it onto this record.
    icon: file-arrow-up
    webhook:
      topic: app.bar.triggered
      deliveryMode: SERVERLESS
      serverlessFunction:
        name: upload-cv
    settingsSchema:
      - name: cv_file
        label: CV
        type: FILE
        required: true
```

`label` is the button. `actionLabel` is the primary button in the dialog.
`tooltipLabel` is the hover / overflow tooltip.

The function receives the usual install envelope (`installationToken`,
`caraerApiBase`, installation `settingsSchema`) plus app-bar fields:


```json
{
  "event": "app.bar.triggered",
  "appBarUuid": "…",
  "appBarLabel": "Upload CV",
  "location": "RECORD_OVERVIEW",
  "recordUuid": "…",
  "object": "candidate",
  "viewId": "…",
  "trait": "…",
  "appBarSettingsValues": { "cv_file": "files/…" },
  "appBarSettingsSchema": [{ "name": "cv_file", "type": "FILE", "value": "files/…" }],
  "installationToken": "inst_…",
  "caraerApiBase": "https://api.caraer.com/api",
  "settingsSchema": []
}
```

Read the dialog from `appBarSettingsValues`. `settingsSchema` is still the
**installation** settings — serverless delivery would overwrite a single
`settingsSchema` with those, so the dialog is keyed separately.

Resolve a `FILE` / `MULTI_FILE` value with
`GET {caraerApiBase}/v2/files/?key=<value>` (absolute URLs pass through).

## Iframe bars

Set `iframeUrl`. Placeholders Caraer substitutes:

`{recordUuid}`, `{object}`, `{viewId}`, `{trait}`, `{companyUuid}`


```yaml
appBars:
  - name: inbox_panel
    location: RECORD_DETAIL
    label: Inbox
    iframeUrl: https://apps.example.com/inbox?record={recordUuid}&object={object}
```


```yaml
appBars:
  - name: calendar_tool
    location: TOOL_BAR
    label: Calendar
    tooltipLabel: Google Calendar
    iframeUrl: https://apps.example.com/calendar?company={companyUuid}
```

Caraer opens a short-lived iframe session. The embedded page can validate it
and receive context (`recordUuid`, `object`, `viewId`, `trait`, `companyUuid`,
`userUuid`) plus `apiToken` — the same short-lived `inst_…` Bearer used in
webhooks. Call Caraer APIs with `Authorization: Bearer` plus that token.

## Authoring notes

- Put `appBars` in `src/app/app.caraer.yaml`. Push with
`caraer apps push --deploy`.
- Give each bar a stable `name` so push can reuse the existing node without
a hand-written `uuid`.
- Wire `webhook.serverlessFunction.name` to a local function folder.
- Action bars need a webhook. Iframe bars need `iframeUrl`.
- Do not put `caraer_api_base` or other platform URLs in bar settings. The
runtime injects `installationToken` / `apiToken` and `caraerApiBase`.
- Hide a bar by leaving its install visibility empty — do not delete the bar
from the manifest unless you mean to remove it for every install.


## Related reading

- [How to create a Caraer app](/blog/2026-08-23-how-to-create-a-caraer-app)
- [Serverless functions in Caraer apps](/blog/2026-03-25-serverless-functions-in-caraer-apps)
- [Caraer CLI](/apis/cli)