Smart Order Capture

Workflow DSL

The JSON shape every workflow takes — the single source of truth shared between the web builder, the API validator, and the on-device Kotlin interpreter.

Workflow shape

{
  "dslVersion": 1,
  "name": "Morning routine",
  "description": "Open Spotify, wait, show a brief.",
  "nodes": [
    {
      "id": "t1",
      "kind": "trigger.time",
      "cron": "0 7 * * 1-5",
      "timezone": "America/New_York"
    },
    { "id": "a1", "kind": "action.openApp", "packageName": "com.spotify.music" },
    { "id": "a2", "kind": "action.wait", "durationMs": 60000 },
    { "id": "a3", "kind": "action.showToast", "message": "Good morning" }
  ],
  "edges": [
    { "id": "e1", "source": "t1", "target": "a1" },
    { "id": "e2", "source": "a1", "target": "a2" },
    { "id": "e3", "source": "a2", "target": "a3" }
  ],
  "enabled": true
}

Top-level fields

  • dslVersion (number, required) — must equal the current DSL version.
  • name (string, 1..120) — human label.
  • description (string?, ≤2000) — optional detail.
  • nodes (Node[], 1..200) — at least one trigger required.
  • edges (Edge[], ≤400) — directed connections between node ids.
  • enabled (boolean) — whether the workflow is armed. Defaults to true.

Node shape

Every node carries an id, an optional label and position, and a kind discriminator that determines which further fields are valid.

Edge shape

{
  "id": "edge_id",
  "source": "<node id>",
  "target": "<node id>",
  "branch": "true" | "false"
}

Variables and branches

An HTTP call with storeResponseAs: "foo" populates a variable in run scope. Downstream branch nodes read it:

{
  "id": "branch1",
  "kind": "action.branch",
  "condition": {
    "variable": "responseBody",
    "op": "contains",
    "value": "\"ok\":true"
  }
}

Operators: eq, neq, gt, gte, lt, lte, contains, matches (regex).

Trace shape

Runs produce an ordered list of events:

{
  "at": 1778953600421,
  "nodeId": "a1",
  "kind": "action.openApp",
  "outcome": "ok",
  "durationMs": 234
}

outcome is one of ok, skipped or failed.

Validation rules worth knowing

  • Node ids must be unique within a workflow.
  • Every edge must reference node ids that exist.
  • At least one trigger node is required.
  • Package names must match the Android package format.
  • Denylisted packages are refused.

Versioning

New node kinds bump DSL_VERSION. If the Kotlin interpreter meets a version it doesn't know, it logs a warning and refuses to execute the run, prompting the user to update the app. Older workflows keep validating against newer versions as long as their kinds are still supported.

JSON schema

The canonical definition lives in packages/shared/src/dsl.ts.