Flow Scheduler API¶
The Flow Scheduler API (/fss/v1) lets scripts, CI pipelines and orchestrators run Tableau Prep flows, follow and cancel runs, and manage tasks, chains and schedules with an API client token.
Base URL and headers¶
Every call goes to the MetaMiner Server (MMS) listener, not to the Flow Scheduler Service (FSS) itself:
https://<mms-host>:<port>/fss/v1/...
The default MMS port is 49714. Use http:// only when SSL is not enabled on MMS. In a high-availability pair, call the load balancer address.
| Header | Value |
|---|---|
Authorization |
Bearer <token>, the token of an API client (see Manage API Clients) |
Content-Type |
application/json on every call with a body |
Requests and responses are JSON. A request body larger than 64 KiB is refused with 413 before the token is checked. Run, task, chain, schedule, parameter and notification calls need the Flow Scheduler switched on and running; they fail until it is.
Who may call¶
| Call | Allowed API clients |
|---|---|
GET /fss/v1/info |
Any API client |
| Runs, tasks, chains, parameters, notifications | Any API client, for the environments and sites it is limited to |
| Change or run a schedule | An admin client without site limits (403 FORBIDDEN otherwise); reading schedules is open to any client |
| Cancel a run | An admin client: any run of its environments and sites. Any other client: a run it started, or a run of a task or chain it owns |
| Run a flow, task or chain as a non-admin client | The client's bound Tableau user (tableauUsername) must be allowed to run every flow involved (403 RUN_FORBIDDEN otherwise) |
The API never takes the caller's identity from the request. createdBy, updatedBy and triggeredBy are always the API client's name, and ownerUsername is ignored for a non-admin client. A Tableau permission change can take up to a minute to reach the run-permission check.
API client management and the audit are on Clients and Audit API.
Addressing¶
| Item | How you name it |
|---|---|
Environment (env) |
The MetaMiner environment id, or its name (not case-sensitive). The environment must be enabled for the Flow Scheduler (see Allowed Environments). A body field on run calls; a query parameter (or body field) on task, chain and schedule calls. |
Site (site) |
The Tableau site LUID. Required to run a flow and to create a task or chain. |
| Flow | The Tableau flow LUID, as Tableau REST returns it. |
| Output steps | The output step's numeric id, its name, or the output-step LUID that Tableau REST returns. |
| Runs, tasks, chains, schedules | Flow Scheduler numeric ids, not Tableau LUIDs. |
A missing env answers 400 BAD_REQUEST; an unknown one 404 ENV_NOT_FOUND. A client limited to sites must name its site (site=) when it lists tasks, chains or runs (400 SITE_REQUIRED). A call that names a task, chain or step by id is checked against the site that object is in (403 SITE_FORBIDDEN), and paths not tied to one site answer 403 SITE_SCOPE to such a client.
Service info¶
GET /fss/v1/info - any API client.
Returns apiVersion ("1"), authMode (TOKEN for API clients, NONE for Local only), the list of endpoints, and, when the Flow Scheduler answers, this node's role and the leader. It answers with the Flow Scheduler switched off too, so it is the simplest token check.
| Status | Meaning |
|---|---|
200 |
Info returned |
401 / 503 |
See Status Codes and Errors |
curl -sS "https://<mms-host>:<port>/fss/v1/info" -H "Authorization: Bearer <token>"
Invoke-RestMethod -Uri "https://<mms-host>:<port>/fss/v1/info" `
-Headers @{ Authorization = "Bearer <token>" }
Run a flow¶
POST /fss/v1/runflow - any API client allowed the environment and site.
Runs a Tableau Prep flow by LUID under the environment's service account, with one-off parameter values and outputs.
| Field | Required | Meaning |
|---|---|---|
env |
Yes | Environment id or name |
site |
Yes | Tableau site LUID |
flowLuid |
Yes | Tableau flow LUID |
flowName |
No | Name recorded with the run |
parameters |
No | [{"name": "...", "value": "..."}] (or "id" instead of "name") |
outputSteps |
No | Outputs to run (id, name or LUID). Absent = every output. |
runMode |
No | full, incremental or default (the flow's own setting in Tableau). Any case. |
wait |
No | true = wait for the outcome |
waitTimeoutSeconds |
No | How long to wait: default 900, 1 to 3600 |
Without wait, the answer is 202 with {"status": "submitted", "runId", "envId", "flowLuid", "tableauJobId", "poll"}. The poll value is an older path that still works; GET /fss/v1/runs/{runId} returns the same run.
With wait: true, the answer carries the run summary (runId, status, tableauJobId, startedAt, completedAt, triggeredBy).
| Status | Meaning |
|---|---|
202 |
Submitted (no wait) |
200 |
wait: the run finished with success or coalesced |
400 |
BAD_REQUEST: body not JSON, env, site or flowLuid missing, or an invalid value such as runMode |
403 |
ENV_FORBIDDEN, SITE_FORBIDDEN, RUN_FORBIDDEN |
404 |
ENV_NOT_FOUND |
409 |
NOT_LEADER (standby node), NOT_LEADER_OR_ACTIVE (the Flow Scheduler refused the submit with a conflict) |
502 |
wait: the run failed or was cancelled. Without wait: TABLEAU_UNAVAILABLE or SUBMIT_FAILED |
504 |
wait: still running at the timeout; the run continues, poll it |
When the Flow Scheduler refuses the submit (here and on runtask), MMS passes on its HTTP status and body; error holds the code named in these tables only when that body has no error text of its own.
curl -sS -X POST "https://<mms-host>:<port>/fss/v1/runflow" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"env": "<envId>", "site": "<site-luid>", "flowLuid": "<flow-luid>",
"parameters": [{"name": "Region", "value": "East"}],
"outputSteps": ["<output-name>"], "runMode": "incremental",
"wait": true, "waitTimeoutSeconds": 600}'
$body = @{
env = "<envId>"; site = "<site-luid>"; flowLuid = "<flow-luid>"
parameters = @(@{ name = "Region"; value = "East" })
outputSteps = @("<output-name>"); runMode = "incremental"
wait = $true; waitTimeoutSeconds = 600
} | ConvertTo-Json -Depth 4
Invoke-RestMethod -Method Post -Uri "https://<mms-host>:<port>/fss/v1/runflow" `
-Headers @{ Authorization = "Bearer <token>" } -ContentType "application/json" -Body $body
Run a task or chain¶
POST /fss/v1/runtask - any API client allowed the environment and the task's site.
Runs a configured task or chain now, under the environment's service account.
| Field | Required | Meaning |
|---|---|---|
env |
Yes | Environment id or name |
taskId or linkedTaskId |
Exactly one | The task id, or the chain id |
runMode |
No | full, incremental or default; overrides the task for this run only |
wait, waitTimeoutSeconds |
No | As for runflow (default 900, 1 to 3600) |
Without wait, the answer is 202 with {"status": "submitted", "envId", "taskId" or "linkedTaskId", "runId", "poll"}. If the run id is not known yet, runId and poll are left out; list the task's runs to find it. With wait: true, the answer is the run summary, as for runflow.
| Status | Meaning |
|---|---|
202 |
Submitted |
200 |
wait: success or coalesced |
400 |
BAD_REQUEST: body not JSON, env missing, both or neither of taskId / linkedTaskId, invalid runMode |
403 |
ENV_FORBIDDEN, SITE_FORBIDDEN, RUN_FORBIDDEN |
404 |
ENV_NOT_FOUND, TASK_NOT_FOUND, LINKED_TASK_NOT_FOUND, FLOW_NOT_FOUND |
409 |
NOT_LEADER (standby node), NOT_LEADER_OR_ACTIVE (the task is already running) |
502 |
wait: failed or cancelled. Without wait: SUBMIT_FAILED |
504 |
wait: still running at the timeout |
curl -sS -X POST "https://<mms-host>:<port>/fss/v1/runtask" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"env": "<envId>", "taskId": <task-id>, "wait": true, "waitTimeoutSeconds": 1800}'
$body = @{ env = "<envId>"; taskId = <task-id>; wait = $true; waitTimeoutSeconds = 1800 } | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri "https://<mms-host>:<port>/fss/v1/runtask" `
-Headers @{ Authorization = "Bearer <token>" } -ContentType "application/json" -Body $body
Read a run¶
GET /fss/v1/runs/{runId} - any API client. Optional query parameter env.
A client sees only runs of its own environments and sites; any other run reads as not found.
| Field | Meaning |
|---|---|
runId, status, runMode |
The run, its status value and run mode |
startedAt, completedAt, triggeredBy |
Times and the API client or user that started it |
tableauJobId, tableauFlowRunId |
Tableau's job and flow-run ids |
outputs |
Per output: stepId, stepLuid, stepName, status, rowCount |
parameters |
The values the run was submitted with: id, name, value |
error |
{code, message, details} when the run failed or was cancelled, else null |
steps |
For a chain: each step with stepOrder, flowId, flowName, runId, status (also skipped), error, outputs |
tableauFlowRunId and outputs appear when MetaMiner can read them from the Tableau Server repository.
| Status | Meaning |
|---|---|
200 |
Run returned |
400 |
BAD_REQUEST: run id not a number |
403 |
ENV_FORBIDDEN: the env named is not allowed for this client |
404 |
FLOW_NOT_FOUND: no such run visible to this client |
curl -sS "https://<mms-host>:<port>/fss/v1/runs/<run-id>" -H "Authorization: Bearer <token>"
Invoke-RestMethod -Uri "https://<mms-host>:<port>/fss/v1/runs/<run-id>" `
-Headers @{ Authorization = "Bearer <token>" }
List runs¶
GET /fss/v1/runs?env=<envId> - any API client allowed the environment.
| Query parameter | Meaning |
|---|---|
env |
Required. Environment id or name. |
site |
Only runs of this site |
taskId, linkedTaskId, flowId |
Only runs of this task, chain or flow |
status |
Comma list of status values |
triggeredBy |
Only runs started by this client or user |
startedAfter, startedBefore |
ISO-8601 times, for example 2026-10-01T00:00:00Z |
sort |
startedAt or -startedAt (newest first) |
offset, limit |
Paging. limit default 50, 1 to 500 (outside that range: 400, not clamped) |
The answer is {"runs": [...], "total", "offset", "limit"}. Each run has runId, status, taskId, linkedTaskId, flowId, flowName, tableauJobId, triggerType, triggeredBy, startedAt, completedAt. The list holds runs the Flow Scheduler started; runs started elsewhere in Tableau are not listed.
| Status | Meaning |
|---|---|
200 |
List returned |
400 |
BAD_REQUEST: env missing or not found, unknown status value, invalid filter or limit; SITE_REQUIRED for a site-limited client without site |
403 |
ENV_FORBIDDEN, SITE_FORBIDDEN |
curl -sS "https://<mms-host>:<port>/fss/v1/runs?env=<envId>&status=failed&startedAfter=2026-10-01T00:00:00Z&sort=-startedAt&limit=20" \
-H "Authorization: Bearer <token>"
$uri = "https://<mms-host>:<port>/fss/v1/runs?env=<envId>&status=failed&startedAfter=2026-10-01T00:00:00Z&sort=-startedAt&limit=20"
(Invoke-RestMethod -Uri $uri -Headers @{ Authorization = "Bearer <token>" }).runs
Cancel a run¶
POST /fss/v1/runs/{runId}/cancel - see Who may call.
Optional body: {"env": "<envId>", "reason": "<text>"}. A chain stops and skips its remaining steps.
The answer is {"runId", "status": "cancelling" or "cancelled", "poll"}. A run in cancelling reads cancelled once Tableau has stopped it.
| Status | Meaning |
|---|---|
200 |
Cancel accepted |
400 |
BAD_REQUEST: run id not a number, body not JSON |
403 |
ENV_FORBIDDEN, CANCEL_FORBIDDEN (not this client's run) |
404 |
RUN_NOT_FOUND, ENV_NOT_FOUND |
409 |
ALREADY_FINISHED, NOT_LEADER |
502 |
CANCEL_FAILED |
curl -sS -X POST "https://<mms-host>:<port>/fss/v1/runs/<run-id>/cancel" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"reason": "Upstream data late"}'
Invoke-RestMethod -Method Post -Uri "https://<mms-host>:<port>/fss/v1/runs/<run-id>/cancel" `
-Headers @{ Authorization = "Bearer <token>" } -ContentType "application/json" `
-Body '{"reason": "Upstream data late"}'
Run status values¶
The API reports these values. The statuses the web interface shows are on Run History.
| Value | Meaning |
|---|---|
queued |
Accepted, not yet started in Tableau |
running |
Running in Tableau, or waiting for Tableau or MMS |
success |
Finished; every output succeeded |
failed |
Failed, refused by Tableau, or an output failed (see error and outputs) |
cancelled |
Cancelled, from the Flow Scheduler or from Tableau |
coalesced |
Not started because the same flow was already running |
server_down |
Held while Tableau Server is unreachable |
skipped |
Chain steps only: not run because the chain stopped |
Tasks, chains and schedules¶
These calls take env as a query parameter (or body field). Create calls for tasks and chains also need site.
| Method and path | What it does |
|---|---|
GET /fss/v1/tasks |
List tasks |
POST /fss/v1/tasks |
Create a task |
PUT /fss/v1/tasks/{id} |
Change a task |
DELETE /fss/v1/tasks/{id} |
Delete a task |
POST /fss/v1/tasks/toggle/{id} |
Enable or disable: {"enabled": false, "reason": "..."} |
GET /fss/v1/linkedtasks, GET /fss/v1/linkedtasks/{id} |
List chains, read one |
POST /fss/v1/linkedtasks |
Create a chain |
PUT /fss/v1/linkedtasks/{id}, DELETE /fss/v1/linkedtasks/{id} |
Change, delete a chain |
POST /fss/v1/linkedtasks/toggle/{id} |
Enable or disable a chain |
GET /fss/v1/schedules, GET /fss/v1/schedules/{id} |
List schedules, read one |
POST /fss/v1/schedules |
Create a schedule (admin client without site limits) |
PUT /fss/v1/schedules/{id}, DELETE /fss/v1/schedules/{id} |
Change, delete a schedule (admin client without site limits) |
GET /fss/v1/schedules/{id}/next-runs?count=<n> |
Preview the next run times (default 5) |
POST /fss/v1/schedules/enabled/{id} |
Suspend or resume one schedule: {"enabled": false} |
POST /fss/v1/schedules/enabled |
Suspend or resume up to 500 schedules: {"ids": [...], "enabled": false}. All or nothing: an unknown id changes nothing (404). |
POST /fss/v1/schedules/{id}/runnow |
Run every enabled task and chain on the schedule now; ?dryRun=true only lists them |
GET, PUT /fss/v1/taskparameters?taskId=<id> |
Flow parameter values of a task |
GET, PUT /fss/v1/stepparameters?stepId=<id> |
Flow parameter values of a chain step |
GET, PUT /fss/v1/notifications/tasks/{id} |
E-mail notifications of a task |
GET, PUT /fss/v1/notifications/linkedtasks/{id} |
E-mail notifications of a chain |
Body fields¶
| Object | Fields |
|---|---|
| Task | scheduleId (required), flowId (flow LUID), flowName, projectName, outputSteps, runMode, enabled (default true), ownerUsername (admin clients only) |
| Chain | name, scheduleId, stopOnFailure, enabled, steps: [{"stepOrder", "flowId", "flowName", "runMode"}] |
| Schedule | name, frequency (HOURLY, DAILY, WEEKLY, MONTHLY), startTime (HH:MM or HH:MM:SS), enabled; hourly: recurrenceMinutes (15, 30, 60, 120 or 240), optional endTime and daysOfWeek; weekly: daysOfWeek (MON to SUN); monthly: dayOfMonth (1 to 31, 0 = last day) |
Schedules run in the environment's time zone; a timezone field is ignored. See Schedules.
Flow parameters¶
GET returns the stored values (params) and a flowParameterDigest of the flow's declared parameters. Send both back with PUT. If the flow's parameters changed since your GET, the save is refused with 409; read again and resend.
Responses¶
The Flow Scheduler answers these calls itself; its error bodies are {"error": "<text>"}. Refusals made before the call reaches it use the codes below.
| Status | Meaning |
|---|---|
200 / 201 |
Done; the object is returned |
400 |
BAD_REQUEST (missing env or site, body not a JSON object, invalid field), SITE_REQUIRED |
403 |
ENV_FORBIDDEN, SITE_FORBIDDEN, SITE_SCOPE, FORBIDDEN (schedule change by a client that may not) |
404 |
ENV_NOT_FOUND, NOT_FOUND (unknown resource or object) |
409 |
NOT_LEADER (standby node); the Flow Scheduler's own conflicts |
# Create a daily schedule, then a task on it
curl -sS -X POST "https://<mms-host>:<port>/fss/v1/schedules?env=<envId>" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"name": "nightly", "frequency": "DAILY", "startTime": "02:30"}'
curl -sS -X POST "https://<mms-host>:<port>/fss/v1/tasks?env=<envId>&site=<site-luid>" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"scheduleId": <schedule-id>, "flowId": "<flow-luid>", "flowName": "Sales prep"}'
$h = @{ Authorization = "Bearer <token>" }
$base = "https://<mms-host>:<port>/fss/v1"
$sched = Invoke-RestMethod -Method Post -Uri "$base/schedules?env=<envId>" -Headers $h `
-ContentType "application/json" -Body '{"name": "nightly", "frequency": "DAILY", "startTime": "02:30"}'
$task = @{ scheduleId = $sched.id; flowId = "<flow-luid>"; flowName = "Sales prep" } | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri "$base/tasks?env=<envId>&site=<site-luid>" -Headers $h `
-ContentType "application/json" -Body $task
Writes in a high-availability pair¶
The Flow Scheduler leader node serves writes. Every /fss/v1 write that reaches the standby (runs, cancels, tasks, chains, schedules, parameters, notifications, API clients) answers 409 NOT_LEADER before anything is checked or recorded:
{ "error": "NOT_LEADER", "message": "writes are served by the Flow Scheduler leader node", "leader": "<leader-host>" }
Send it again through the load balancer or to the leader. leader is unknown when this node cannot read the Flow Scheduler role. See High Availability.