Authorize

#Canonical Example — the Tasks domain

This is the full worked example. It threads the concept guides together on one tiny, generic domain so you can see how a data model, a workflow, an app spec, and cross-tenant sharing fit into a single build. Each step links to the guide that covers it in depth; the snippets here carry only the concrete Tasks requests unique to the example.

Everything is a live API call against https://api.helm.bridge-labs.com, authenticated with a Personal Access Token over HTTP Basic auth (base64(client_id:client_secret)).

The domain is intentionally minimal:

Project ──< Task >── Assignee

#1. Data model

Three entity types, each with a JSON attribute schema and a stable identifier key:

  • Project{ key, name, status }, identified by key.
  • Task{ key, title, state, priority }, identified by key, with state and priority indexed so they're filterable/sortable.
  • Assignee{ email, name }, identified by email.

Two relationship rules connect them: ProjectToTask (one-to-many) and TaskToAssignee (many-to-one). The flow is always schema → entity type → relationship rule → entities → links — see Entities & Data Model for the full mechanics.

Register the Task attribute schema first:

POST/schema/schemasRegister the Task schemaAPI docs ↗Try it

Then bind it to an entity type, declaring the key attribute as its stable identifier key (what a bulk upsert matches an existing row on) and which attributes are indexed:

POST/entity/entity-typesCreate the Task entity typeAPI docs ↗Try it

Repeat for Project and Assignee, then create the two relationship rules (/entity/relationship-rules) that link them.

#2. Workflow — an inline action and a referenced job

The workflow onboard-project runs two job steps:

  1. Inline job — an upsertEntity action that creates a "Kickoff" Task for the new project, authored directly on the workflow step.
  2. Referenced job — invokes the reusable complete-task job (defined once under /definition/jobs), which marks that task done.

Inline keeps step-local logic next to the workflow; a referenced job is reusable and version-pinned across workflows. Smart values thread the trigger payload through both steps. See Workflows and Smart Values — a step's output binds as outputs.<stepId>.

First the reusable job:

POST/definition/jobsDefine the complete-task jobAPI docs ↗Try it

Then the workflow that creates the kickoff task inline and calls that job:

POST/definition/workflowsDefine the onboard-project workflowAPI docs ↗Try it

#3. Activation & monitoring

A definition is draft until it's activated for your tenant — activation merges env, reconciles the trigger rows, and binds the access policy each run acts under. It's idempotent. See Triggers.

POST/workflows/{id}/activateActivate onboard-projectAPI docs ↗Try it

Each invocation produces a run you can inspect. List runs filtered by workflow:

GET/workflows/runs/workflowsList runs for a workflowAPI docs ↗Try it

Pass workflow_id as a query parameter, then drill into a single run for its per-job and per-action detail via GET /workflows/runs/workflows/{id}.

#4. App spec — a dashboard with live widgets

The app spec (the spec_document body on the wire) defines one dashboard, Tasks, with a board page composed of three widgets:

  • a header that renders from config alone,
  • a table of the project's tasks, fed by a page-data $graphql snapshot,
  • an actions widget exposing an "Add task" command (an inline upsertEntity).

The full widget set — light widgets that render from config and heavy widgets that need authored sample data — is documented, with live previews, in the Widget Catalogue. Here is the header rendered live:

Loading live widget preview…
{
  "widgetName": "header",
  "config": {
    "text": "Tasks",
    "headerLevel": 1
  }
}

See App spec structure for every spec_document part.

#5. App activation (config, integration, token)

Activating the spec for a tenant creates the activation chain (see Apps (Bridge)):

  1. PUT /apps/{specId}/config — the tenant's app config.
  2. Register the provider company, then POST /apps/{specId}/integrations — the integration that callers run against.
  3. PUT /apps/{specId}/integrations/{integrationId}/token — the run token, captured once.

Registering the provider company is a one-time onboarding step performed by a platform operator, not a regular customer call. The general API exposes read access to companies (GET /access/external-companies); the create path lives on the operator surface and is done as part of onboarding before you wire the integration.

#6. User provisioning

When the provider is configured to auto-create users, the first POST /apps/run for a new provider user (the integration is identified by app-context headers, not a path segment) provisions the tenant user. You can then assign that user to a spec policy (PUT /apps/{specId}/users/{authUserId}/policy). See Provisioning.

#7. Shares & grants

To let a principal read Tasks within your tenant, create an intra-tenant grant (tier-2) on the Task entity type:

POST/access/grantsGrant read on the Task typeAPI docs ↗Try it

To let another tenant build on this — activate the shared spec, or store their own Tasks of the shared type — the owner adds a read-only share (tier-1). Cross-tenant data reads need both a share and a grant. See Shares & Grants.

#8. GraphQL → workflow

The table's data comes from a $graphql query over Task (queried via POST /entity/graphql; root fields are PascalCase, operators are underscore-prefixed, and filterable attributes nest under attributes:). The same query can be turned into a workflow queryEntity action step with the interactive converter at /graphql/ — paste a GraphQL query and copy the emitted config straight into a workflow. See Querying & GraphQL.