Run actions

Call a single catalogue action as an ad-hoc SDK Execution.

Use lunnoa.actions.run when you need one app action without building a workflow graph. The platform creates a one-step Execution with source: SDK, runs the action through the same path as workflow steps, then finalises that Execution. These examples assume an authenticated lunnoa client from the overview.

Discover app and action IDs

Catalogue metadata (including each action's inputConfig) comes from the CLI or workflowApps.list(). Prefer npx @lunnoa/client tools list --json then tools get <actionId> --json when a coding agent is choosing appId and actionId. lunnoa.actions.get(appId, actionId) only looks up one action you already know.

discover-actions.ts
const apps = await lunnoa.workflowApps.list();const outlook = apps.find((app) => app.id === 'microsoft-outlook');const slack = apps.find((app) => app.id === 'slack');// Each action exposes id, name, description, needsConnection, inputConfigfor (const action of (outlook?.actions as Array<{ id?: string; name?: string }> | undefined) ?? []) {  console.log(action.id, action.name);}// Thin helper when you already know both IDs:// const def = await lunnoa.actions.get(//   'microsoft-outlook',//   'microsoft-outlook_action_get-email-by-id',// );

See also Discovery.

Run an action

actions.run returns the Execution id plus the synchronous status, output, and single-step executionPath. Both examples below use real catalogue apps that need a Connection instance UUID (connectionId). Copy it from Connections in the product UI, or from lunnoa.connections.list().

Read Outlook mail

Retrieves one message with microsoft-outlook_action_get-email-by-id. Message IDs usually come from an Outlook Email Received trigger payload or from Microsoft Graph.

run-outlook-get-email.ts
const result = await lunnoa.actions.run({  appId: 'microsoft-outlook',  actionId: 'microsoft-outlook_action_get-email-by-id',  name: 'Read Outlook message',  connectionId: 'YOUR_OUTLOOK_CONNECTION_UUID',  input: {    messageId: 'AAMkAGI2TG93AAA=',    downloadAttachments: 'false',  },});console.log(result.id, result.status, result.output);for (const step of result.executionPath ?? []) {  console.log(`[${step.status}] ${step.label}`);}

Send a Slack message

Posts to a channel with slack_action_send-message-to-channel. Pass the Slack channel ID (for example C0123456789), not the #channel-name. Invite the Lunnoa bot to the channel first (/invite @Lunnoa Labs).

run-slack-send-message.ts
const result = await lunnoa.actions.run({  appId: 'slack',  actionId: 'slack_action_send-message-to-channel',  name: 'Notify team in Slack',  connectionId: 'YOUR_SLACK_CONNECTION_UUID',  input: {    channelId: 'C0123456789',    message: 'Deploy finished successfully.',  },});console.log(result.id, result.status, result.output);

Connection UUID rules

When an action has needsConnection: true, pass the Connection instance UUID (Connection.id), not the catalogue connection-type key.

SituationBehaviour
connectionId providedUsed after auth checks (canUseConnection) and app match
Omitted, exactly one connection for that workflowAppIdThat connection is used
Omitted, multiple connections400 listing that connectionId is required (fail-closed; no silent “oldest wins”)
Omitted, none400 asking you to create a connection or pass connectionId

Copy the UUID from Connections in the product UI (table ID column or connection detail page), or from lunnoa.connections.list().

Project scoping follows the same fail-closed pattern: pass projectId when the workspace has more than one project.

Poll like any other Execution

The run appears in executions.list with source: SDK. Filter with filterBy: ['source:SDK'] when you only want ad-hoc runs.

poll-adhoc.ts
const again = await lunnoa.executions.get(result.id, {  expansion: ['status', 'source', 'name', 'output', 'executionPath'],});// Or wait/poll with backoff (useful if you only kept the id):// const done = await lunnoa.actions.runAndWait({ appId, actionId, input });

Render progress from executionPath, the same as workflow runs. See Workflows.

Permissions

POST /api/actions/run is Public API (@PublicApi): API keys (lna_…) and user JWTs both work. The caller needs workflows:execute (plus connection access when a connection is used).

Related