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.
| 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.
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.
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, andTRAIT_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.
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.
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: truelabel 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:
{
"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).
Set iframeUrl. Placeholders Caraer substitutes:
{recordUuid}, {object}, {viewId}, {trait}, {companyUuid}
appBars:
- name: inbox_panel
location: RECORD_DETAIL
label: Inbox
iframeUrl: https://apps.example.com/inbox?record={recordUuid}&object={object}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.
- Put
appBarsinsrc/app/app.caraer.yaml. Push withcaraer apps push --deploy. - Give each bar a stable
nameso push can reuse the existing node without a hand-writtenuuid. - Wire
webhook.serverlessFunction.nameto a local function folder. - Action bars need a webhook. Iframe bars need
iframeUrl. - Do not put
caraer_api_baseor other platform URLs in bar settings. The runtime injectsinstallationToken/apiTokenandcaraerApiBase. - 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.