# Workflow automation

Build, review and run effects with conditions, inputs and revision control.

**Review means automatic rules.** A workflow reads its inputs and data, filters records, and follows
the matching Condition path. It does not wait for a person to approve a run. Schedule that workflow
to check those rules periodically, or run it from an existing button or form.

## Build the logic

The action canvas has twelve generic operations. Choose every field, condition and destination
explicitly; there are no built-in customer, account or support rules.

| Node               | Purpose                                                                       |
| ------------------ | ----------------------------------------------------------------------------- |
| Start              | Configure triggers, declare typed inputs and optionally choose a database.    |
| Saved query        | Reuse a saved visual or native query and bind its parameters.                 |
| SQL / native query | Read with the database's native language in the middle of a workflow.         |
| Filter records     | Keep records matching AND/OR/NOT conditions in an already fetched list.       |
| Map values         | Build values or records from inputs, earlier results and expressions.         |
| Validate           | Stop when a required rule fails.                                              |
| Condition          | Choose the true or false path automatically.                                  |
| HTTP request       | Call a REST endpoint with a method, URL, headers and optional JSON/text body. |
| Send notification  | Send a bound title/message through existing notification channels.            |
| Insert record      | Map fields for one new PostgreSQL record.                                     |
| Update record      | Identify one PostgreSQL record and verify original values before changing it. |
| Return result      | End a path and optionally return a value.                                     |

For example: **Start → Saved query → Filter records → Condition**. The true path can call an API
and notify a channel; the false path can go straight to Return result. The user supplies the rules.
Filters operate on the returned batch. Put aggregation and filtering in the query when a decision
needs the full database population; query row limits and truncation metadata still apply.

SQL parameters use `:name` and explicit value bindings. PostgreSQL and MySQL retain their existing
positional syntax too; one statement cannot mix positional and named parameters. The shared lexer
preserves strings, comments and casts, and drivers receive values separately. SQLite uses its native
named bindings. Non-SQL native steps use their source language; use Saved query for dynamic parameters
on those sources. Native steps are reads: use record steps for database mutations.

HTTP supports GET, POST, PUT, PATCH, DELETE and HEAD. Its result exposes `body`, `status` and `ok`.
Enable **Continue with error responses** to route non-success status codes through a Condition.
Transport failures still stop the step. Bearer, Basic and API-key headers can be saved through the
encrypted vault after saving the draft. Credentials belong to that workflow and exact API origin;
definitions contain only the reference. Ordinary authored headers are visible in the definition.
Requests do not follow redirects. The existing outbound-network policy permits public HTTPS;
desktop/self-hosted deployments can opt in to internal HTTP with `BYTESMAP_NOTIFY_ALLOW_INTERNAL`.

Opening, editing, saving and publishing a workflow does not execute its queries, requests or messages.
Publication checks paths, read-only native syntax, bindings, permissions and destinations.
The HTTP timeout is editable from 1–120 seconds. Request and response bodies are bounded to 1 MiB.

## Configure triggers on Start

Select the workflow's **Start → Triggers & inputs**, either in its own editor or expanded inside a
View. Both use the same trigger editor. Publish the current draft before adding an automatic trigger.
A workflow can be called from a View button or the API, and can also have schedule and data-condition
triggers. They all execute the same published workflow through the same runtime.

**Incoming API request → Connection details** shows the published workflow's endpoint and a
copyable HTTP example with its typed inputs. It uses the normal `POST /api/actions/:id/run`
operation and a workspace API key with Run and dependency access. A webhook sender can call it
when it supports a custom authorization header and JSON body; fixed third-party payloads need an
application adapter. Set `requestKey` to the incoming event/delivery ID and reuse the same key and
payload on retries. Viewing or copying the example does not execute the workflow. Draft changes do
not change the shown input contract, and the request's revision remains explicitly pinned.

**Schedule trigger** runs with configured starting inputs. **Data condition** checks a saved query
and runs the workflow when that query returns at least one record. Choose the output for a visual
data flow and bind its parameters. Define filters and aggregate thresholds in the query builder;
for example, return a row only when the number of backend errors exceeds your threshold. A plain
`COUNT(*)` returns a row even when its value is zero, so add the threshold condition in that query.

The default **Once when the condition becomes true** runs on the first matching check, then waits
for a nonmatching check before it can run again. **Once when the condition stops being true** waits
for a matching check, then runs once when a later check returns no records. An initially empty result
does not fire a clearing trigger. **Every check while the condition is true** runs on each matching
check. Checks that do not meet the selected transition appear in history
as **Condition not triggered**. A matched check consumes its transition even if the action fails;
review the failed run before using Run now to retry. Manual Run now evaluates the condition too,
ignores the previous match, and leaves the automatic transition state unchanged. For a clearing
trigger, Run now runs only when there are no matching records. Calling the workflow
directly from a button or API is an independent trigger and does not evaluate its automatic conditions.

Saving a data trigger snapshots its query and checks permissions without reading customer data.
Later query edits do not silently change an enabled trigger. Edit and save the trigger to adopt the
current saved query. Query and source access are checked again at execution time, including every
source in a connected data flow. Removing the query or revoking access stops the check before effects.

Select an interval, daily, weekly or monthly cadence for running the action or checking its condition. Calendar schedules
use an IANA time zone and follow daylight saving. Interval schedules accept 1–43,200 minutes.
Dashboard delivery uses the existing schedule editor with dashboard panels, filters and channels.

Saving an enabled schedule authorizes future runs; it does not run immediately. The three-dot menu
provides Edit, Run now, Run history, Pause and Delete. Editing a paused schedule can enable it again.
Workflow schedules pin an immutable published revision. Republishing the workflow does not silently
change an existing automation; **Use latest publication** explicitly selects the newer version and
shows its input contract before saving.

The sidebar project filters the collection. Ownership and access come from the saved target's project
and item grants. Opening a schedule does not change the selected project. Editing requires Write on
its workflow or dashboard, plus the normal execution permissions for its dependencies. Individual
sharing continues to work without a project grant. Schedules introduce no second permission system.

Automatic execution uses the user and originating credential that last saved the definition.
Only membership/credential IDs are persisted; every run resolves their current grants. Manual Run now
uses its requester. Revoking membership, project/item access or a token prevents later effects. A
removed and re-added membership does not revive old automation authority. API keys, CLI, native/HTTP
SDK and MCP all use the same operations; configuring channels and workspace settings remains an
account-admin action.

## Execution and delivery

One scheduler and durable queue handle both workflow runs and dashboard deliveries. The existing
`report_schedule` / `report_run` tables gain an exclusive workflow-or-dashboard target and origin
references; existing report IDs, settings and history remain intact. Dashboard-specific `reports.*`
operations delegate to the same records and delivery services used by `schedules.*`.
Data-condition transition state is persisted with the same durable claim and completed run. No
separate condition scheduler or action executor is used.

Database-only PostgreSQL workflows retain one transaction. A workflow containing HTTP or notification
steps commits database mutations in separate transactions, without holding a database lock across
network calls. Earlier external effects cannot be rolled back. A failed or interrupted run with a
possible committed effect is marked **Check outcome**, and its schedule pauses rather than repeating
effects automatically. The same action request key cannot execute twice. Review history and the
destination before starting a new request.

Normal failures record the failed run; later scheduled occurrences can check again. Missed intervals
collapse into one catch-up run. A scheduled workflow has a five-minute execution deadline and a
ten-minute durable lease. Queued Run now requests survive restarts. Interrupted workflow leases pause
the schedule. History stores timing, status and the workflow activity ID, not returned database rows.

**Settings → Notifications → Scheduler & channel delivery** controls both schedule types. Pausing
automatic checks leaves Run now available. BytesMap's API/service must remain running; the browser
can close. Desktop uses its in-process scheduler. A separate worker uses the existing
`bun run --cwd apps/api reports:worker` entry point with shared PostgreSQL metadata. See
[dashboard delivery and worker setup](https://bytesmap.com/docs/automate/reports.md) for configuration.

## Shared API

| Method               | Path                      | Operation                                                              |
| -------------------- | ------------------------- | ---------------------------------------------------------------------- |
| GET                  | `/api/schedules`          | List authorized schedules and available targets; optional `projectId`. |
| POST                 | `/api/schedules`          | Create a workflow or dashboard schedule.                               |
| GET / PATCH / DELETE | `/api/schedules/:id`      | Read, edit/pause or delete.                                            |
| POST                 | `/api/schedules/:id/run`  | Queue an explicit run; returns 202.                                    |
| GET                  | `/api/schedules/:id/runs` | Recent outcomes and workflow activity IDs.                             |

```ts
await client.schedules.create({
  kind: 'workflow',
  actionId: workflow.id,
  enabled: true,
  definition: {
    name: 'Check open cases',
    revision: workflow.publishedRevision,
    inputs: { state: 'open' },
    // Optional: omit for an unconditional scheduled run.
    condition: {
      queryId: matchingCasesQuery.id,
      params: { state: 'open' },
      mode: 'enter', // 'exit' when a match clears; 'while' on every matching check
      // outputNodeId: 'threshold', // required for a saved visual flow
    },
    recurrence: {
      frequency: 'interval',
      intervalMinutes: 15,
      time: '09:00',
      timezone: 'UTC',
      weekday: 1,
      monthDay: 1,
    },
  },
})
```

Data conditions are periodic checks, not database change capture or an event-stream subscription.
HTTP initiation uses the authenticated API rather than a separate public webhook receiver. Loops,
per-record fan-out, cross-database transactions and compensating external actions are later
extensions. Existing single-record PostgreSQL mutation limits remain in effect.

Integration tests in `packages/platform/test/workflow-automation.test.ts` exercise real isolated
PostgreSQL queries/mutations and a local HTTP server, with captured notification delivery. Browser
tests in `apps/dashboard/e2e/automation-schedules.e2e.ts` cover authoring, encrypted settings,
schedule management and execution history. They never use customer sources or real recipients.
