{"templateId":"../@theme/Templates/BlogWithAuthors","sharedDataIds":{"sidebar":"sidebar-blog/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Two kinds","description":"Developer documentation for the Caraer platform. REST API reference, Bearer authentication, tutorials, and guides for records, forms, webpages, webhooks, and automations.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Define them in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.caraer.yaml"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appBars"]},"). There is no"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caraer apps add app-bar"]}," scaffold. Push deploys the bar; the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["installer"]}," ","chooses where it is visible (which objects, or which suites for the sidebar"," ","tool)."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you have not scaffolded an app yet, start with"," ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-08-23-how-to-create-a-caraer-app"},"children":["How to create a Caraer app"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"two-kinds","__idx":0},"children":["Two kinds"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Kind"},"children":["Kind"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Locations"},"children":["Locations"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What the user sees"},"children":["What the user sees"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What you ship"},"children":["What you ship"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Action"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_PREVIEW"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_OVERVIEW"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_TRAIT"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A button. Click opens a dialog, then a webhook runs"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook"]}," + optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["settingsSchema"]},". No ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["iframeUrl"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Iframe"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_DETAIL"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOOL_BAR"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TRAIT_BAR"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["An embedded page"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["iframeUrl"]}," (required). Webhook is optional"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Action bars must not set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["iframeUrl"]},". Iframe bars must set it. The platform"," ","rejects the other combination."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"where-they-appear","__idx":1},"children":["Where they appear"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"location"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["location"]}]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"In the Caraer app"},"children":["In the Caraer app"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Context passed through"},"children":["Context passed through"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_PREVIEW"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Action buttons under the record preview card"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current record"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_OVERVIEW"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Action buttons in the object view header (list / board)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current object, view, and trait"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_TRAIT"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Action buttons on a trait overview (page cards now; other traits later)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current record, object, view, and trait"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_DETAIL"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Embedded iframe on the record detail page"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current record"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOOL_BAR"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Tool entry in the left sidebar, with the other tools"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Company (no record)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TRAIT_BAR"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Extra tab next to the record's traits"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Current view and trait"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["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."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"who-sees-a-bar","__idx":2},"children":["Who sees a bar"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Declaring ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appBars"]}," on the app is not enough. After install, visibility lives"," ","on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HAS_APP"]}," edge (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appBarVisibility"]},"), keyed by app-bar UUID:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Record locations"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_PREVIEW"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_OVERVIEW"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_DETAIL"]},","," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TRAIT_BAR"]},") — list of ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["object names"]},". The bar shows only on those"," ","objects. An empty list means hidden."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RECORD_TRAIT"]}]}," — ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["object names"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["trait names"]},". Both lists must"," ","match the current object and trait. Installers can currently pick ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["page"]},";"," ","other traits will be added when those overviews have a mount. An empty"," ","traits list hides the bar."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOOL_BAR"]}]}," — list of ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["suites"]},". The bar shows when the company has one"," ","of those suites. An empty list means hidden."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Installers set this in ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Settings → Apps"]}," on the installation. Authors do"," ","not hard-code object names in the manifest for placement."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"action-bars","__idx":3},"children":["Action bars"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Give the bar a webhook. Topic is always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.bar.triggered"]}," — not"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["record.{object}.created"]},". Delivery is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SERVERLESS"]}," (a function in your app)"," ","or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HTTP"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["settingsSchema"]}," on the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["bar"]}," (not the app) is the dialog the user fills"," ","before the webhook runs. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FILE"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MULTI_FILE"]}," belong here, not on"," ","installation settings."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"appBars:\n  - name: upload_cv\n    location: RECORD_OVERVIEW\n    label: Upload CV\n    actionLabel: Parse CV\n    tooltipLabel: Parse a CV onto this candidate\n    description: Upload a CV. Caraer will parse it onto this record.\n    icon: file-arrow-up\n    webhook:\n      topic: app.bar.triggered\n      deliveryMode: SERVERLESS\n      serverlessFunction:\n        name: upload-cv\n    settingsSchema:\n      - name: cv_file\n        label: CV\n        type: FILE\n        required: true\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["label"]}," is the button. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["actionLabel"]}," is the primary button in the dialog."," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tooltipLabel"]}," is the hover / overflow tooltip."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The function receives the usual install envelope (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["installationToken"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caraerApiBase"]},", installation ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["settingsSchema"]},") plus app-bar fields:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"event\": \"app.bar.triggered\",\n  \"appBarUuid\": \"…\",\n  \"appBarLabel\": \"Upload CV\",\n  \"location\": \"RECORD_OVERVIEW\",\n  \"recordUuid\": \"…\",\n  \"object\": \"candidate\",\n  \"viewId\": \"…\",\n  \"trait\": \"…\",\n  \"appBarSettingsValues\": { \"cv_file\": \"files/…\" },\n  \"appBarSettingsSchema\": [{ \"name\": \"cv_file\", \"type\": \"FILE\", \"value\": \"files/…\" }],\n  \"installationToken\": \"inst_…\",\n  \"caraerApiBase\": \"https://api.caraer.com/api\",\n  \"settingsSchema\": []\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read the dialog from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appBarSettingsValues"]},". ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["settingsSchema"]}," is still the"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["installation"]}," settings — serverless delivery would overwrite a single"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["settingsSchema"]}," with those, so the dialog is keyed separately."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Resolve a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FILE"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MULTI_FILE"]}," value with"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET {caraerApiBase}/v2/files/?key=<value>"]}," (absolute URLs pass through)."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"iframe-bars","__idx":4},"children":["Iframe bars"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["iframeUrl"]},". Placeholders Caraer substitutes:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{recordUuid}"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{object}"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{viewId}"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{trait}"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{companyUuid}"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"appBars:\n  - name: inbox_panel\n    location: RECORD_DETAIL\n    label: Inbox\n    iframeUrl: https://apps.example.com/inbox?record={recordUuid}&object={object}\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"appBars:\n  - name: calendar_tool\n    location: TOOL_BAR\n    label: Calendar\n    tooltipLabel: Google Calendar\n    iframeUrl: https://apps.example.com/calendar?company={companyUuid}\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Caraer opens a short-lived iframe session. The embedded page can validate it"," ","and receive context (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recordUuid"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["object"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["viewId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["trait"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["companyUuid"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userUuid"]},") plus ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["apiToken"]}," — the same short-lived ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["inst_…"]}," Bearer used in"," ","webhooks. Call Caraer APIs with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: Bearer"]}," plus that token."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authoring-notes","__idx":5},"children":["Authoring notes"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Put ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["appBars"]}," in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["src/app/app.caraer.yaml"]},". Push with"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caraer apps push --deploy"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Give each bar a stable ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["name"]}," so push can reuse the existing node without"," ","a hand-written ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["uuid"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Wire ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook.serverlessFunction.name"]}," to a local function folder."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Action bars need a webhook. Iframe bars need ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["iframeUrl"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Do not put ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caraer_api_base"]}," or other platform URLs in bar settings. The"," ","runtime injects ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["installationToken"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["apiToken"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caraerApiBase"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["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."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-reading","__idx":6},"children":["Related reading"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-08-23-how-to-create-a-caraer-app"},"children":["How to create a Caraer app"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-03-25-serverless-functions-in-caraer-apps"},"children":["Serverless functions in Caraer apps"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/apis/cli"},"children":["Caraer CLI"]}]}]}]},"headings":[{"value":"Two kinds","id":"two-kinds","depth":2},{"value":"Where they appear","id":"where-they-appear","depth":2},{"value":"Who sees a bar","id":"who-sees-a-bar","depth":2},{"value":"Action bars","id":"action-bars","depth":2},{"value":"Iframe bars","id":"iframe-bars","depth":2},{"value":"Authoring notes","id":"authoring-notes","depth":2},{"value":"Related reading","id":"related-reading","depth":2}],"frontmatter":{"title":"App bars in Caraer apps","authors":["sem_tadema"],"tags":["documentation","app"],"template":"../@theme/Templates/BlogWithAuthors","seo":{"title":"Two kinds"}},"lastModified":"2026-08-23T15:16:11.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/blog/2026-08-23-app-bars-in-caraer-apps","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}