{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/usewrit-app/json-schema/usewrit-app-workflow-schema.json",
  "title": "Workflow",
  "description": "A redacted workflow row. `credentials_encrypted` NEVER appears; instead the daemon computes `has_credentials`, `credential_keys` (names only), `placeholders` (the non-secret run-time inputs) and `has_login`. JSON-TEXT columns (`steps`, `raw_replay`, `form_data`, `exit_condition`, `input_rules`, `api_functions`, `streaming_config`, `functions`, `login_url_patterns`) are re-hydrated to real JSON values on output (a malformed legacy value stays a string).",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/writ-agent.yaml#/components/schemas/Workflow",
  "type": "object",
  "required": [
    "id",
    "name",
    "workflow_type",
    "steps",
    "timeout_ms",
    "retry_count",
    "headless",
    "fast_mode",
    "is_active",
    "is_verified",
    "schedule_enabled",
    "session_persistence",
    "relogin_max_retries",
    "http_capable",
    "usage_count",
    "total_run_count",
    "total_failure_count",
    "consecutive_failures",
    "cloud_callable",
    "created_at",
    "has_credentials",
    "credential_keys",
    "placeholders",
    "has_login"
  ],
  "properties": {
    "id": {
      "type": "integer",
      "format": "int64"
    },
    "name": {
      "type": "string"
    },
    "description": {
      "type": [
        "string",
        "null"
      ]
    },
    "workflow_type": {
      "type": "string",
      "description": "`recorded` | `pre_check` | `on_change` | `api_recorded` | `streaming`."
    },
    "steps": {
      "description": "Recorded step array (re-hydrated JSON; a malformed legacy value stays a string)."
    },
    "raw_replay": {
      "description": "Raw replay JSON (re-hydrated) or null."
    },
    "form_data": {
      "description": "Declared input map (re-hydrated) or null."
    },
    "exit_condition": {
      "description": "Exit-condition JSON (re-hydrated) or null."
    },
    "input_rules": {
      "description": "Input-rule JSON (re-hydrated) or null."
    },
    "api_functions": {
      "description": "API-function JSON (re-hydrated) or null."
    },
    "streaming_config": {
      "description": "Streaming-config JSON (re-hydrated) or null."
    },
    "functions": {
      "description": "Callable-function manifest JSON (re-hydrated) or null."
    },
    "entry_url": {
      "type": [
        "string",
        "null"
      ]
    },
    "timeout_ms": {
      "type": "integer",
      "format": "int64"
    },
    "retry_count": {
      "type": "integer",
      "format": "int64"
    },
    "headless": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "fast_mode": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "is_active": {
      "type": "integer",
      "description": "0/1 integer flag (always 1 — deactivation is retired)."
    },
    "is_verified": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "schedule_enabled": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "schedule_interval_ms": {
      "type": [
        "integer",
        "null"
      ],
      "format": "int64"
    },
    "schedule_kind": {
      "type": [
        "string",
        "null"
      ],
      "description": "`interval` (default) | `daily` | `weekly`."
    },
    "schedule_time": {
      "type": [
        "string",
        "null"
      ],
      "description": "\"HH:MM\" local wall-clock fire time (daily/weekly)."
    },
    "schedule_days": {
      "type": [
        "string",
        "null"
      ],
      "description": "JSON array string of ISO weekday ints, 1=Mon … 7=Sun (weekly only)."
    },
    "schedule_tz": {
      "type": [
        "string",
        "null"
      ],
      "description": "IANA tz name for daily/weekly (null ⇒ UTC)."
    },
    "last_scheduled_at": {
      "type": [
        "string",
        "null"
      ]
    },
    "next_scheduled_at": {
      "type": [
        "string",
        "null"
      ]
    },
    "session_persistence": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "session_ttl_seconds": {
      "type": [
        "integer",
        "null"
      ],
      "format": "int64"
    },
    "login_url_patterns": {
      "description": "Login-URL pattern JSON (re-hydrated) or null."
    },
    "relogin_max_retries": {
      "type": "integer",
      "format": "int64"
    },
    "http_capable": {
      "type": "integer",
      "description": "Browserless HTTP-lane hint: -1 unknown, 0 browser-only, 1 HTTP-proven."
    },
    "auth_config": {
      "type": [
        "string",
        "null"
      ],
      "description": "Declarative AuthRecipe JSON as stored TEXT (not re-hydrated)."
    },
    "default_persona_id": {
      "type": [
        "integer",
        "null"
      ],
      "format": "int64"
    },
    "estimated_duration_ms": {
      "type": [
        "integer",
        "null"
      ],
      "format": "int64"
    },
    "usage_count": {
      "type": "integer",
      "format": "int64"
    },
    "total_run_count": {
      "type": "integer",
      "format": "int64"
    },
    "total_failure_count": {
      "type": "integer",
      "format": "int64"
    },
    "consecutive_failures": {
      "type": "integer",
      "format": "int64"
    },
    "last_run_at": {
      "type": [
        "string",
        "null"
      ]
    },
    "last_run_status": {
      "type": [
        "string",
        "null"
      ],
      "description": "Status of the most recent run (computed; null when never run)."
    },
    "last_run_duration_ms": {
      "type": [
        "integer",
        "null"
      ],
      "format": "int64"
    },
    "last_run_has_extracted_data": {
      "type": [
        "integer",
        "null"
      ],
      "description": "0/1 — whether a successful run produced a non-trivial payload (computed)."
    },
    "last_failure_at": {
      "type": [
        "string",
        "null"
      ]
    },
    "last_failure_error": {
      "type": [
        "string",
        "null"
      ]
    },
    "cloud_callable": {
      "type": "integer",
      "description": "0/1 integer flag."
    },
    "execution_target": {
      "type": [
        "string",
        "null"
      ],
      "description": "Run-venue pin (`local` | `cloud`); null = Auto."
    },
    "marketplace_slug": {
      "type": [
        "string",
        "null"
      ],
      "description": "Set when this row is a proxy for an installed marketplace listing."
    },
    "created_at": {
      "type": "string"
    },
    "updated_at": {
      "type": [
        "string",
        "null"
      ]
    },
    "has_credentials": {
      "type": "boolean",
      "description": "Whether a sealed credential blob exists (the blob itself is never returned)."
    },
    "credential_keys": {
      "type": "array",
      "description": "NAMES of the stored credential map's top-level keys (values never returned). Degrades to [] when the vault is locked or the blob is unreadable.",
      "items": {
        "type": "string"
      }
    },
    "placeholders": {
      "type": "array",
      "description": "The non-secret run-time inputs the Run modal renders.",
      "items": {
        "$ref": "#/$defs/WorkflowPlaceholder"
      }
    },
    "has_login": {
      "type": "boolean",
      "description": "Deterministic \"does this workflow authenticate?\" heuristic."
    },
    "marketplace": {
      "description": "GET /v1/workflows/{id} only, for marketplace PROXY rows: the install's attach projection (`{slug, listing_title, manifest, bindings}` — names only), or `{slug, missing: true}` when the install row is gone.",
      "type": "object",
      "additionalProperties": true
    }
  },
  "$defs": {
    "WorkflowPlaceholder": {
      "type": "object",
      "description": "One run-time input field descriptor.",
      "required": [
        "key",
        "label",
        "field_type"
      ],
      "properties": {
        "key": {
          "type": "string",
          "description": "The placeholder name (e.g. `city` or `input.city`)."
        },
        "label": {
          "type": "string",
          "description": "Humanized label (`input.` prefix stripped, underscores → spaces)."
        },
        "field_type": {
          "type": [
            "string",
            "null"
          ],
          "description": "Field-type hint (null when unknown; marketplace slots may set it)."
        }
      }
    }
  }
}