Elizon Docs
Workflows

Apps overview

How App steps work — one palette entry per app, a connected account, a resource and an operation.

The Apps row of the palette holds one entry per app Elizon knows: Google Calendar, Google Contacts, Google Sheets, Google Drive and — where the deployment offers it — Gmail. Drag the app you want, pick the account it should use, then pick what it should do.

Gmail may not be in your palette, and that is not a bug.

Gmail needs a Google permission that Google classifies as restricted: an app may only ask for it after a security review, and Elizon's own review is still in progress. Until it completes, Gmail is off on Elizon's hosted service and the palette does not show it.

A self-hosted Elizon using its own Google OAuth client is its own publisher and can turn Gmail on: add google-gmail to WORKFLOW_APPS_ENABLED (and to NEXT_PUBLIC_WORKFLOW_APPS_ENABLED, so Studio shows it) and restart. With it off, a Gmail step is not droppable, is absent from the app list, and a Package containing one fails to install with that explanation rather than installing a step whose every run refuses.

Each app is one entry in the palette, but they are all the same kind of step underneath — which is why a new app arrives as a new palette entry and nothing else changes. Nothing about the way you configure Google Sheets is different from the way you configure Google Calendar.

Configuration

Every App step is the same node (app) underneath:

FieldTypeDefaultDescription
appstring''The app: google-calendar, google-contacts, google-sheets, google-drive or google-gmail.
versionstring''The app definition's version the step was built against.
resourcestring''What it acts on — an event, a contact, a range, a file. Each app's page lists them.
operationstring''What it does to it. Each app's page lists them, with their inputs.
connectionIdstring''The connected account, from Connections.
inputsmap{}One entry per input of the operation: { value, valueMode }, where valueMode is fixed or expression.

Picking the account

The account list holds the connected accounts available to this workflow: the ones in its project, plus your organization's organization-level ones — see Connections. Only accounts for the right service are offered: a Google Calendar step offers your Google accounts and not an API key you keep for something else.

One Google account serves every Google app. If a step needs a permission that account has not granted, the node says which one before the run rather than failing with Google's own "insufficient authentication scopes" — reconnect the account under Settings → Connections and grant it.

Your tokens never leave Elizon's own backend. The step names the account; the credential is resolved, refreshed and applied where it is stored, and the run record never contains it.

Picking what it does

Two lists, in order: a resource (a calendar, an event, a contact, a spreadsheet, rows) and then an operation on it (list, get, create, update, delete, look up). Choosing the operation builds the form below it — one field per input that operation takes, with the field's own type, its help text and the same Fixed / Expression toggle the rest of the canvas uses.

Some fields are filled from the app itself. A Calendar step's Calendar field lists your own calendars by name, fetched with the account you picked. Pick the account first; until you do, the field says so rather than showing an empty list.

An operation that deletes something says so under the operation list. Elizon does not stop you — it is a step you asked for — but it will not let you place one without reading that it cannot be undone.

Output

FieldTypeDescription
statusnumberThe HTTP status the app answered with.
itemCountnumberItems returned, for a list operation.
pagesnumberPages fetched.
truncatedbooleantrue when the page cap stopped a list while the app had more.
appstringThe app.
resourcestringThe resource.
operationstringThe operation.

The answer itself is under result, and a file answer under file:

{{ upstream.<nodeId>.output.result }}

result is the answer, already flattened: an event's start and end are strings rather than the nested objects Google sends. A list operation returns an array, so {{ upstream.<nodeId>.output.result }} feeds a Loop directly, and the expression picker offers the fields that operation actually returns.

Beside it: itemCount, pages, and truncated. A list operation follows the app's own paging up to a cap; truncated is true when the cap stopped it while the app still had more, so a partial answer is never silently partial. Narrow the request (a date window, a filter) rather than raising the cap.

A delete answers with nothing, so result is null. That is the operation working.

An operation whose answer is a file rather than data — Google Drive's Download a file — fills {{ upstream.<nodeId>.output.file }} instead, and leaves result as null. The file is the same kind of thing a Convert to File or a form upload produces, so anything that takes a file takes this one: an AI Prompt attachment, a Send Email attachment, an FTP upload, a Drive upload. It is kept for 30 days and deleted with its run, like every other file a workflow makes.

Limits and errors

  • A step whose account has not granted the operation's permission says which one before the run.
  • A list operation stops at its page cap and reports truncated: true rather than returning a silently partial answer.
  • An app turned off on this deployment (Gmail, until Google's review completes) cannot be dropped on the canvas, and a Package containing it fails to install with that explanation.

Example

workflows/next-events.yaml
name: Next events
description: Lists the upcoming events in the primary calendar.
graph:
  nodes:
    - id: start
      type: trigger_manual
      position: { x: 0, y: 0 }
      data: { description: 'Run it from Studio.' }
    - id: next-events
      type: app
      position: { x: 280, y: 0 }
      data:
        app: google-calendar
        version: 1.0.0
        resource: event
        operation: list
        connection: google-account
        inputs:
          calendarId: { value: 'primary', valueMode: fixed }
          timeMin: { value: '{{ run.startedAt }}', valueMode: expression }
  edges:
    - { id: start-to-next-events, source: start, target: next-events }

In a Package

An App step travels whole, including its account — and it is the only integration step that does. The account becomes a credential Slot: the Package declares that it needs, say, a Google account, and whoever installs it binds one of their own already-connected accounts to that Slot in the install wizard (or names it by id from the CLI). Nothing about your account travels.

That is the difference between an account and a secret. An HTTP Request node's {{secret.KEY}} points into your vault, so a same-named secret in someone else's organization would be a different value — which is why it is not carried. A connected account is an object the installer already has, so there is something real to bind.

npx @elizon/cli validate checks this offline, before you sign anything: a step bound to a Slot the Package does not declare, a Slot that is not a credential Slot, or one that asks for the wrong service all fail the build. And if the app's provider is missing from your compliance.providers, validate warns — the install review is where an operator reads which third parties a Package sends their data to, and a Google integration declaring none tells them the wrong thing.

elizon.yaml
slots:
  - id: google-account
    type: credential
    scheme: oauth2
    app: google
compliance:
  providers: [google]
  • Travels: app, version, resource, operation, inputs
  • Converted: connectionId → connection (the credential Slot)
  • Never carried: none

The apps