Studio

Flow SDK

Build, test, and publish reusable JavaScript/TypeScript artifacts that extend FlowOS workflows, connectors, and automation logic.

Overview

The Flow SDK lets developers write custom logic as artifacts — TypeScript/JavaScript functions that run inside the FlowOS execution engine. Artifacts can be custom workflow nodes, connector actions, data transformers, or scheduled jobs. They are published as versioned packages to your workspace artifact registry and can be reused across all workflows and apps.

Key Concepts

FieldTypeRequiredDefaultDescription
ArtifactResourceoptionalA versioned TypeScript/JS module — function, connector, or transform
PackResourceoptionalA bundle of related artifacts distributed as a single installable unit
ModuleResourceoptionalA named namespace grouping artifacts for organization
TestResourceoptionalA unit/integration test case for an artifact with assertions
ScheduleResourceoptionalA cron-based scheduled invocation of an artifact

Quickstart: Write a custom node

1. Install the CLI

bash
npm install -g @flowos/cli
flowos login --instance https://acme.flowos.io
flowos init my-artifact --type node
cd my-artifact

2. Write the artifact

src/index.ts
import { defineNode, NodeContext } from '@flowos/sdk'

export default defineNode({
  id: 'acme.enrichTicket',
  name: 'Enrich Ticket',
  description: 'Add SLA deadline and owner from CMDB lookup',
  version: '1.0.0',

  inputs: {
    ticketId: { type: 'string', required: true },
    severity:  { type: 'enum', values: ['P1','P2','P3','P4'], required: true },
  },

  outputs: {
    slaDeadline:   { type: 'timestamp' },
    ownerEmail:    { type: 'string' },
    ownerTeam:     { type: 'string' },
  },

  async execute(input, ctx: NodeContext) {
    const slaDurations = { P1: 1, P2: 4, P3: 24, P4: 72 }
    const hoursToAdd = slaDurations[input.severity]

    const deadline = new Date()
    deadline.setHours(deadline.getHours() + hoursToAdd)

    // Look up CMDB ownership
    const cmdb = await ctx.tables.get('cmdb_services', {
      filters: { ticketTags: { contains: input.ticketId } }
    })

    const owner = cmdb?.records[0]

    ctx.log('info', `Enriched ticket ${input.ticketId} with SLA: ${deadline.toISOString()}`)

    return {
      slaDeadline: deadline.toISOString(),
      ownerEmail: owner?.ownerEmail ?? null,
      ownerTeam:  owner?.ownerTeam  ?? null,
    }
  },
})

3. Write a test

src/index.test.ts
import { testNode } from '@flowos/sdk/testing'
import node from './index'

describe('acme.enrichTicket', () => {
  it('sets P1 SLA to 1 hour from now', async () => {
    const result = await testNode(node, {
      input: { ticketId: 'INC-001', severity: 'P1' },
      mockTables: {
        cmdb_services: { records: [] }
      }
    })

    const now = Date.now()
    const deadline = new Date(result.slaDeadline).getTime()
    expect(deadline - now).toBeGreaterThan(59 * 60 * 1000)
    expect(deadline - now).toBeLessThan(61 * 60 * 1000)
    expect(result.ownerEmail).toBeNull()
  })
})

4. Publish

bash
flowos test          # run test suite
flowos publish       # bumps version, bundles, and uploads to registry

# Output
✓ Tests passed (3/3)
✓ Bundle size: 12.4 kB
✓ Published: acme/enrichTicket@1.0.0
✓ Available in Workflow Studio > Custom Nodes

Artifacts API

GET
/api/flowsdk/artifacts

List all artifacts in the workspace

GET
/api/flowsdk/artifacts/:id

Get artifact detail (fetch versions and test cases via the endpoints below)

POST
/api/flowsdk/artifacts/:id/execute

Execute an artifact directly with a given input payload

GET
/api/flowsdk/artifacts/:id/test-cases

List test cases for an artifact

POST
/api/flowsdk/artifacts/:id/test-cases/run-all

Run all of the artifact's test cases

GET
/api/flowsdk/artifacts/:id/versions

List published versions

POST
/api/flowsdk/artifacts/:id/restore/:versionNumber

Restore the artifact to a previous version

Execute an artifact via API

bash
POST /api/flowsdk/artifacts/64f1a2b3c4d5e6f7a8b9c0d1/execute
{
  "inputPayload": {
    "ticketId": "INC-1042",
    "severity": "P1"
  },
  "triggerType": "manual_execute:enrichTicket"
}

// Response
{
  "success": true,
  "data": {
    "aborted": false,
    "abortReason": null,
    "current": { "ticketId": "INC-1042", "severity": "P1" },
    "previous": null,
    "output": {
      "slaDeadline": "2026-06-01T11:00:00Z",
      "ownerEmail": "infra-oncall@acme.com",
      "ownerTeam": "Infrastructure"
    },
    "runtimeError": null,
    "messages": [],
    "uiStateChanges": []
  }
}

Packs

Packs bundle multiple artifacts into a single installable unit. FlowOS ships a set of baseline and sample-data packs (e.g. core_baseline_pack, itsm_baseline_pack) that you install per workspace.

GET
/api/flowsdk/installs

List installed baseline and sample-data packs

POST
/api/flowsdk/install-packs

Install a baseline pack (packKey: 'core_baseline_pack' | 'itsm_baseline_pack')

pack.json
{
  "id": "acme/itsm-enrichment",
  "name": "ITSM Enrichment Pack",
  "version": "2.1.0",
  "description": "Custom nodes for SLA enrichment and CMDB lookups",
  "author": "acme-engineering@acme.com",
  "artifacts": [
    "acme.enrichTicket",
    "acme.cmdbLookup",
    "acme.slaCalculator"
  ],
  "dependencies": {
    "@flowos/sdk": "^1.0.0"
  },
  "engines": { "flowos": ">=1.0.0" }
}

Scheduled Jobs

Artifacts can be scheduled to run on a cron schedule independently of workflows. Useful for periodic data sync, health checks, or report generation.

Scheduled jobs are just Flow SDK artifacts with artifactType: 'scheduled_job' — there is no separate scheduled-jobs resource:

GET
/api/flowsdk/artifacts?artifactType=scheduled_job

List scheduled-job artifacts

POST
/api/flowsdk/artifacts

Create a scheduled-job artifact

PATCH
/api/flowsdk/artifacts/:id

Update the schedule or script

POST
/api/flowsdk/scheduled-jobs/run-due

Execute all scheduled jobs currently due to run

DELETE
/api/flowsdk/artifacts/:id

Delete a scheduled-job artifact

bash
POST /api/flowsdk/artifacts
{
  "name": "Nightly SLA Report",
  "artifactType": "scheduled_job",
  "moduleKey": "core",
  "status": "active",
  "detail": {
    "cron": "0 6 * * *",          // 6am UTC daily
    "timezone": "UTC",
    "scriptSource": "flow.notify.send({ channel: 'email', to: 'management@acme.com', message: 'Nightly SLA report' })",
    "retryPolicy": { "retries": 2 }
  }
}

NodeContext API Reference

The NodeContext object is passed to every artifact's execute function. It provides access to platform services without requiring a separate API token.

FieldTypeRequiredDefaultDescription
ctx.tablesTablesClientoptionalCRUD access to DB Studio tables in the workspace
ctx.connectorsConnectorClientoptionalInvoke actions on configured connectors
ctx.secretsSecretsClientoptionalRead secrets from the platform vault (write-once)
ctx.eventsEventBusClientoptionalPublish events to the event bus
ctx.httpHttpClientoptionalMake outbound HTTP calls (respects circuit breakers)
ctx.logLogFnoptionalWrite to the run trace log (level, message, metadata)
ctx.workspaceWorkspaceInfooptionalRead-only workspace metadata (id, slug, name)
ctx.environmentstringoptionalCurrent environment: development | staging | production
Artifacts run in a sandboxed Node.js process with a 30-second default timeout (configurable to 300s for compute-heavy tasks). They have no filesystem access — all I/O goes through the ctx API.