Authorize

#Apps (Bridge)

A bridge app is a config-driven UI — dashboards, pages, and widgets — that renders inside a host provider (e.g. a shipping platform's canvas) and reads/writes your Helm entities on behalf of that provider's users. You author an app spec once; each tenant activates it, wires an integration to a provider organisation, and the provider's users get a rendered, permission-scoped app.

An app lives in two layers:

  • Definition — the design-time app spec (spec_document): dashboards, pages, widgets, providers, per-principal config schemas, policies, and shares. Authored under /definition/specs and shareable to other tenants.
  • Activation — a tenant's use of a spec: turning it on, registering provider integrations, minting app tokens, and per-principal config. Driven under /apps/*. Each activation is independent, so the same spec serves many tenants.

Authenticate with a Personal Access Token (HTTP Basic auth) — enter it once in the Authorize bar at the top of this page and every Try it below uses it. Each example body is pre-filled; the console targets staging by default. Full request/response schemas are in the linked API reference.

This guide is for an implementer who authors an app once and activates it for other tenants. It walks the activation happy path; the spec_document shape is detailed below. For the widgets see the Widget Catalogue; for the expression model see Smart Values. How a spec and its manifest provision a consumer tenant is covered in Shares & Grants.

#Build an app

The flow is: (optional) register config schemas → create the spec → activate → register the provider organisation (one-time onboarding) → wire an integration → mint a token → render.

#1. (Optional) Register a principal-config schema

If your spec validates per-principal config (principalConfigSchemas), register the JSON schema in the activating tenant first. Skip this when all three of company/team/user are null.

POST/schema/schemasRegister a principal-config schemaAPI docs ↗Try it

#2. Create the app spec

App specs are part of the definition layer (POST /definition/specs). Inside spec_document, every key is camelCase (defaultDashboard, widgetComponent, autoCreateUsers, …); the one exception is the identity extractor keys (user_external_id, …), which are well-known provider-identity field names.

POST/definition/specsCreate an app specAPI docs ↗Try it

The response is 201 with the spec under data. The policies and shares blocks are optional. The full policy/share story is in Shares & Grants; every spec_document part is detailed in App spec structure.

#3. Activate the spec in a tenant

Activation is per tenant: there is exactly one activation per (spec, tenant), so the call is an upsert — first call creates, later calls update. On creation, the spec's policy block is provisioned into the activating tenant so its users get the access the spec intends (see Shares & Grants).

PUT/apps/{specId}/configActivate the spec in your tenantAPI docs ↗Try it

To override the spec-defined policy, pass policy_id (an existing policy) or policy (an inline definition); the override takes precedence and later spec edits no longer touch this activation.

#4. Register the provider organisation (one-time onboarding)

An app renders for a specific provider organisation — the host platform's tenant, keyed by (provider, external_id), where provider matches a key in spec_document.providers. Registering that organisation is a one-time platform-operator onboarding step, not something a tenant-admin can self-serve: on the public surface /access/external-companies is read-only. Ask your Helm operator to register the organisation against the activating tenant, then read it back to get its id for the next step. See Provisioning.

#5. Wire an integration

An integration ties a registered provider organisation to your activation. Its allowed_origins is both the render-time Origin check and the browser CORS source for /apps/run — a browser host must list its exact origin here.

POST/apps/{specId}/integrationsCreate a provider integrationAPI docs ↗Try it

#6. Mint an app token

App tokens are scoped to a single integration and authenticate the render call. The first issue needs no flag; replacing a live token is explicit (?rotate=true — a plain PUT over an active token returns 409).

PUT/apps/{specId}/integrations/{integrationId}/tokenMint an app tokenAPI docs ↗Try it

The response carries raw_token (helm_at_<64-hex>) exactly once — it is shown once and not retrievable later, so capture it now.

#7. (Optional) Pre-author principal config

Override spec defaults at the company, team, or user level before first use, addressed by natural key. For company the principalId is the provider organisation's id; for user it is the user's id.

PUT/apps/{specId}/principals/{type}/{principalId}Set per-principal configAPI docs ↗Try it

#8. Render the app

The render call uses the app token (Bearer), not your PAT, and includes the integration id in the path — so it is not part of the Authorize bar above. The provider posts the identity payload your spec's identity extractors expect:

curl -X POST https://api.helm.bridge-labs.com/apps/run/<integration_id> \
  -H "Authorization: Bearer helm_at_<your_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "user": { "id": "ext-user-1", "emailAddress": "user@example.com", "firstName": "Jane", "lastName": "Doe" },
    "company": { "id": "sedna-org-12345", "name": "Acme" }
  }'

The 200 carries surfaces (the rendered dashboard), state, and referenceData. A data_grid's rows come back under referenceData keyed by the grid's element id — not inline in the surface.

#App spec structure

The spec_document is a declarative tree — dashboards → pages → widgets — plus the providers, policies, and shares the app binds to. Inside it, every key is camelCase, and the body is validated on create.

spec_document
├── dashboards.<key>        a named dashboard (the unit a user opens)
│   └── pages.<key>         a page within the dashboard
│       └── widgets.<id>    a widget on the page (see the Widget Catalogue)
├── widgets.<id>            SPEC-LEVEL widgets — merged into every page
├── pages.<key>             SPEC-LEVEL shared pages — merged into every dashboard
└── providers, policies, shares, jobs, …

Merge semantics. Spec-level widgets/pages are inherited by every dashboard; a dashboard-level entry of the same key overrides the shared one (so navigate targets resolve against the merged set — no duplicate-modal idiom).

Field Req Purpose
dashboards The dashboards, keyed by name. The only required field.
defaultDashboard Which dashboard renders first (literal key or a {{ }}); falls back to the first key.
widgets / pages Spec-level widgets/pages merged into every dashboard (see merge semantics).
providers External data/identity providers the app binds to (auth, autoCreateUsers, identity).
policies Named access policies; each is provisioned into every consumer tenant on activation.
defaultPolicy Default policy key (must be in policies); applied to the activation after provisioning.
shares Explicit cross-tenant shares — the derived owner manifest is built solely from this block.
jobs Named jobs callable from a job step.
principalConfigSchemas Maps company/team/user to a JSON-schema name its per-principal config must validate against.
functions Spec-defined JSONata functions registered into every expression in this app.
defaults / messages Spec-level config defaults and error-copy overrides.

Access is policy-based, not role-based — there is no top-level roles field. App access is policies + defaultPolicy (provisioned per consumer tenant) plus per-user assignment. Apps that don't need policy-mode authorisation omit policies and fall back to standard mode (the union of the user's role bindings); principalConfigSchemas governs config shape, not access.

#Widget behaviour & smart values

Widgets are config-driven and can carry interactive steps (entity ops, HTTP, setPrincipalConfig), render-time $if visibility, and {{ }} smart values. The full authoring model lives in Smart Values; each widget's config is catalogued in the Widget Catalogue. Two spec-wide authoring vocabularies — data sources (how a widget gets its rows) and commands (what an action does) — are documented next.

#Data sources

A widget that shows data declares a dataSource — a data_grid/table's rows, or a select/form's options. Pick one of four producers, optionally combined with $pipe row-shaping and $dependsOn cross-widget refetch:

Producer Shape Use
$query { "$query": { "type": "Task", "filter"?: {…}, "sort"?: [{…}], "include"?: [...], "single"?: true } } Read entities of a type (filter/sort mirror the entity query).
$http { "$http": { "url": "…", "method"?: "GET", "headers"?: {…}, "rowsPath"?: "data.items" } } Fetch rows from a provider HTTP endpoint (SSRF-safe; JSON only).
$rows { "$rows": [ … ] } An inline / computed array (often { "$jsonata": "page.data.x" }).
$typeSchema { "$typeSchema": { "entityType": "Vessel", "attribute": "segment" } } (or { "schema", "path" }) An option feed built from a schema's enum values.

Combine a producer with these node-level siblings:

  • $pipe — shape the resolved rows (filter/map/orderBy/leftJoin/distinct). The full vocabulary is in Smart Values → Shaping a row set.
  • $dependsOn[ { "widget": "<id>", "field": "<key>" } ]: refetch this source when another widget's value changes (e.g. a grid that filters by a select).
  • single — on $query, return the first row (or null) instead of a one-element array.
"dataSource": {
  "$query": {
    "type": "Task",
    "filter": { "operator": "eq", "path": ["attributes", "status"], "value": "open" },
    "sort": [ { "path": ["attributes", "due_date"], "direction": "asc" } ]
  },
  "$dependsOn": [ { "widget": "status_filter", "field": "value" } ],
  "$pipe": [ { "map": { "title": "{{ attributes.title }}", "due": "{{ attributes.due_date }}" } } ]
}

#Commands

An interactive widget (a button, a form submit, a row action) runs a command — an ordered list of typed steps. Each step has a kind plus its own fields, and may carry stepId (binds its result to outputs.<stepId>), if (a render-time gate), and onSuccess / onError recovery sub-sequences (the same model as a workflow action). The vocabulary:

kind Key fields What it does
navigate page, params?, modal? Go to a page (or open it as a modal).
refresh includeModal? Re-render the current page.
setPageData data, inModal? Write keys into page state (read elsewhere as page.data.*).
setWidgetState widgetId, state Set one widget's state (selection, paging, …).
notify level, message Show a toast.
actions actions[], input?, output? Run orchestration actions inline — the inline-job path for apps.
job job, input? Run a named job definition.
setPrincipalConfig principalType, principalId?, config? / ops? Write per-principal config (company/team/user).
setSecret target, value Store a scoped secret from inside the app (see Secrets).
debug value? Echo a value while authoring.

The actions step runs the same action catalog a workflow job does, inline in the render request — prefer it over a named job step for app-local logic (see Workflows → Jobs & actions).

"command": [
  { "kind": "actions", "actions": [ { "stepId": "save", "name": "upsertEntity", "config": { "typeName": "Task", "payload": "{{ form }}" } } ] },
  { "kind": "notify", "level": "success", "message": "Saved" },
  { "kind": "refresh" }
]

#Providers, tokens & secrets

  • Providers & external users — auto-provisioning, identity extraction, and OIDC vs bearer providers. See Provisioning.
  • App tokens — issue, rotate (?rotate=true), list, and revoke per integration; revocation propagates immediately.
  • Secrets — set a value, then wire its reference into config (the reference is never stored in the spec). See Secrets.

#Share an app with another tenant

Publishing a spec for another tenant to install — spec shares, data shares, and the policy fan-out — is its own topic. See Shares & Grants.

#API reference

Every app spec, config, integration, token, and principal-config operation — with full schemas and an interactive console — is in the API reference.