Skip to main content
CrystalFlow workflows are fully serializable to JSON, enabling version control, sharing, and programmatic manipulation of workflows.

Overview

Every CrystalFlow workflow can be:
  • Serialized to JSON for storage
  • Deserialized from JSON to recreate workflows
  • Validated against a formal JSON Schema
  • Version controlled in Git
  • Shared across teams and applications

Workflow JSON Format

The workflow JSON format follows this structure:
{
  "version": "1.0.0",
  "id": "workflow-abc123",
  "name": "My Workflow",
  "description": "Optional workflow description",
  "nodes": [
    {
      "id": "node-1",
      "type": "math.add",
      "position": { "x": 100, "y": 100 },
      "inputs": { "a": 10, "b": 20 },
      "properties": {}
    }
  ],
  "connections": [
    {
      "id": "conn-1",
      "source": "node-1",
      "sourceOutput": "result",
      "target": "node-2",
      "targetInput": "value"
    }
  ],
  "variables": {
    "apiKey": "your-key",
    "environment": "production"
  }
}

Top-Level Fields

version
string
required
Schema version (currently "1.0.0")
id
string
required
Unique identifier for the workflow
name
string
Human-readable workflow name
description
string
Optional description of the workflow’s purpose
nodes
Node[]
required
Array of node definitions
connections
Connection[]
required
Array of connections between nodes
variables
Record<string, any>
Global variables accessible during execution

Node Format

Each node in the nodes array has this structure:
{
  "id": "node-1",
  "type": "math.add",
  "position": {
    "x": 100,
    "y": 100
  },
  "inputs": {
    "a": 10,
    "b": 20
  },
  "properties": {
    "precision": 2
  },
  "metadata": {
    "label": "Add Numbers",
    "category": "Math"
  }
}

Node Fields

id
string
required
Unique identifier for the node within the workflow
type
string
required
Node type identifier (e.g., "math.add", "http.request")
position
{x: number, y: number}
required
Visual position on the canvas
inputs
Record<string, any>
Input port values (for unconnected inputs)
properties
Record<string, any>
Property values set via @Property decorator
metadata
object
Additional metadata (label, category, description)

Connection Format

Each connection in the connections array:
{
  "id": "conn-1",
  "source": "node-1",
  "sourceOutput": "result",
  "target": "node-2",
  "targetInput": "value"
}

Connection Fields

id
string
required
Unique identifier for the connection
source
string
required
ID of the source node
sourceOutput
string
required
Name of the output port on the source node
target
string
required
ID of the target node
targetInput
string
required
Name of the input port on the target node

Serialization API

To JSON

Convert a workflow to JSON:
import { serializeWorkflow } from '@crystalflow/core';

// Get workflow data
const workflowData = workflow.toJSON();

// Serialize to JSON string
const json = serializeWorkflow(workflowData);

// Save to file
import fs from 'fs';
fs.writeFileSync('workflow.json', json);

From JSON

Load a workflow from JSON:
import { deserializeWorkflow, Workflow } from '@crystalflow/core';
import fs from 'fs';

// Read JSON file
const json = fs.readFileSync('workflow.json', 'utf-8');

// Deserialize
const workflowData = deserializeWorkflow(json);

// Create workflow instance
const workflow = Workflow.fromJSON(workflowData);

Validation

Validate workflow JSON against the schema:
import { validateWorkflowJSON } from '@crystalflow/core';

const json = fs.readFileSync('workflow.json', 'utf-8');
const errors = validateWorkflowJSON(json);

if (errors.length > 0) {
  console.error('Validation errors:', errors);
} else {
  console.log('Workflow JSON is valid!');
}

Complete Example

Here’s a complete workflow JSON with multiple nodes and connections:
workflow.json
{
  "version": "1.0.0",
  "id": "data-processing-workflow",
  "name": "Data Processing Pipeline",
  "description": "Fetches, filters, and transforms data",
  "nodes": [
    {
      "id": "http-fetch",
      "type": "http.request",
      "position": { "x": 100, "y": 100 },
      "properties": {
        "url": "https://api.example.com/data",
        "method": "GET"
      },
      "metadata": {
        "label": "Fetch Data",
        "category": "Network"
      }
    },
    {
      "id": "filter-active",
      "type": "data.filter",
      "position": { "x": 300, "y": 100 },
      "properties": {
        "condition": "item.active === true"
      },
      "metadata": {
        "label": "Filter Active Items",
        "category": "Data"
      }
    },
    {
      "id": "transform",
      "type": "data.map",
      "position": { "x": 500, "y": 100 },
      "properties": {
        "expression": "{ id: item.id, name: item.name }"
      },
      "metadata": {
        "label": "Transform Data",
        "category": "Data"
      }
    },
    {
      "id": "display",
      "type": "output.display",
      "position": { "x": 700, "y": 100 },
      "inputs": {},
      "metadata": {
        "label": "Display Results",
        "category": "Output"
      }
    }
  ],
  "connections": [
    {
      "id": "conn-1",
      "source": "http-fetch",
      "sourceOutput": "response",
      "target": "filter-active",
      "targetInput": "array"
    },
    {
      "id": "conn-2",
      "source": "filter-active",
      "sourceOutput": "filtered",
      "target": "transform",
      "targetInput": "array"
    },
    {
      "id": "conn-3",
      "source": "transform",
      "sourceOutput": "output",
      "target": "display",
      "targetInput": "value"
    }
  ],
  "variables": {
    "apiKey": "your-api-key",
    "environment": "production",
    "maxRetries": 3
  }
}

Loading This Workflow

import { deserializeWorkflow, Workflow, Executor } from '@crystalflow/core';
import fs from 'fs';

// Load from file
const json = fs.readFileSync('workflow.json', 'utf-8');
const workflowData = deserializeWorkflow(json);
const workflow = Workflow.fromJSON(workflowData);

// Execute
const executor = new Executor();
const result = await executor.execute(workflow);

console.log('Workflow executed:', result.status);

Schema Validation

The formal JSON Schema is available at schema/workflow.schema.json in the repository.

Using the Schema

import Ajv from 'ajv';
import schema from '@crystalflow/core/schema/workflow.schema.json';

const ajv = new Ajv();
const validate = ajv.compile(schema);

const workflowData = JSON.parse(json);
const valid = validate(workflowData);

if (!valid) {
  console.error('Validation errors:', validate.errors);
}

Version Control

Store workflows in Git:
# Add workflow to Git
git add workflows/my-workflow.json
git commit -m "Add data processing workflow"

# View changes
git diff workflows/my-workflow.json

# Restore previous version
git checkout HEAD~1 workflows/my-workflow.json

Best Practices

Follow semantic versioning for workflow changes: major.minor.patch
Include workflow and node descriptions for documentation
Always validate JSON before committing to version control
Never hardcode sensitive data - use variables and inject at runtime
Store related workflows in organized directories

Programmatic Workflow Creation

Create workflows programmatically from JSON:
import { Workflow } from '@crystalflow/core';

const workflowData = {
  version: '1.0.0',
  id: 'generated-workflow',
  name: 'Generated Workflow',
  nodes: [
    {
      id: 'node-1',
      type: 'math.add',
      position: { x: 100, y: 100 },
      inputs: { a: 10, b: 20 }
    }
  ],
  connections: [],
  variables: {}
};

const workflow = Workflow.fromJSON(workflowData);

Migration Between Versions

When the schema version changes, use migration utilities:
import { migrateWorkflow } from '@crystalflow/core';

// Migrate from old version to current
const migratedData = migrateWorkflow(oldWorkflowData, {
  from: '0.9.0',
  to: '1.0.0'
});

const workflow = Workflow.fromJSON(migratedData);
Breaking Changes: Schema version 1.0.0 has not been published yet. Until then, breaking changes may occur without migration paths.

Export Formats

Minified JSON

const json = serializeWorkflow(workflowData, { pretty: false });
// Single line, no whitespace

Pretty JSON

const json = serializeWorkflow(workflowData, { pretty: true, indent: 2 });
// Formatted with 2-space indentation

Next Steps