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
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Artifact | Resource | optional | — | A versioned TypeScript/JS module — function, connector, or transform |
| Pack | Resource | optional | — | A bundle of related artifacts distributed as a single installable unit |
| Module | Resource | optional | — | A named namespace grouping artifacts for organization |
| Test | Resource | optional | — | A unit/integration test case for an artifact with assertions |
| Schedule | Resource | optional | — | A cron-based scheduled invocation of an artifact |
Quickstart: Write a custom node
1. Install the CLI
npm install -g @flowos/cli
flowos login --instance https://acme.flowos.io
flowos init my-artifact --type node
cd my-artifact2. Write the artifact
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
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
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 NodesArtifacts API
/api/flowsdk/artifactsList all artifacts in the workspace
/api/flowsdk/artifacts/:idGet artifact detail (fetch versions and test cases via the endpoints below)
/api/flowsdk/artifacts/:id/executeExecute an artifact directly with a given input payload
/api/flowsdk/artifacts/:id/test-casesList test cases for an artifact
/api/flowsdk/artifacts/:id/test-cases/run-allRun all of the artifact's test cases
/api/flowsdk/artifacts/:id/versionsList published versions
/api/flowsdk/artifacts/:id/restore/:versionNumberRestore the artifact to a previous version
Execute an artifact via API
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.
/api/flowsdk/installsList installed baseline and sample-data packs
/api/flowsdk/install-packsInstall a baseline pack (packKey: 'core_baseline_pack' | 'itsm_baseline_pack')
{
"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:
/api/flowsdk/artifacts?artifactType=scheduled_jobList scheduled-job artifacts
/api/flowsdk/artifactsCreate a scheduled-job artifact
/api/flowsdk/artifacts/:idUpdate the schedule or script
/api/flowsdk/scheduled-jobs/run-dueExecute all scheduled jobs currently due to run
/api/flowsdk/artifacts/:idDelete a scheduled-job artifact
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.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ctx.tables | TablesClient | optional | — | CRUD access to DB Studio tables in the workspace |
| ctx.connectors | ConnectorClient | optional | — | Invoke actions on configured connectors |
| ctx.secrets | SecretsClient | optional | — | Read secrets from the platform vault (write-once) |
| ctx.events | EventBusClient | optional | — | Publish events to the event bus |
| ctx.http | HttpClient | optional | — | Make outbound HTTP calls (respects circuit breakers) |
| ctx.log | LogFn | optional | — | Write to the run trace log (level, message, metadata) |
| ctx.workspace | WorkspaceInfo | optional | — | Read-only workspace metadata (id, slug, name) |
| ctx.environment | string | optional | — | Current environment: development | staging | production |
ctx API.