Workflows
Workflows API methods, requests, and responses for RawTree Cloud and RawTree BYOC.
Workflows API
This API manages SQL workflows in RawTree Cloud and RawTree BYOC, where enabled. RawTree executes the SQL. The service manages schedules, runs, and result delivery.
Workflows run SQL on demand or on a schedule. They send read-query results to HTTP, table, or Slack sinks. See the workflow guide for SQL behavior and delivery guarantees.
All workflow endpoints require organization and cluster query parameters,
using their names, and an admin API key, a user session bearer token, or an OAuth
token with full_access. Admin API keys can read and manage workflows only in
their bound organization and cluster; other API key permissions, including custom
database roles, are not supported. Organization members can read workflows, runs,
logs, and metrics; organization administrators can create, run, update, and delete workflows, and cancel runs.
Workflows belong to the organization and execute with organization credentials. Expiring or revoking the API key used to create a workflow prevents further API access with that key but does not stop the workflow. Pause or delete the workflow to stop future scheduled executions.
A disabled workflow service returns 503.
GET /v1/workflows
List workflows in the selected organization and cluster.
{ "workflows": [] }Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
Request:
# List workflows in an organization and cluster.
curl -X GET "https://api.rawtree.com/v1/workflows?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 200.
{
"workflows": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "event-count",
"query": {
"database": "analytics",
"sql": "SELECT count() AS total FROM events"
},
"schedule": null,
"sinks": [],
"revision": 1,
"created_at": "2026-11-09T10:00:00Z",
"updated_at": "2026-11-09T10:00:00Z"
}
]
}Errors: 403, 503. See API errors.
POST /v1/workflows
Create a workflow. Returns 201 with the saved workflow, including its id,
revision, created_at, updated_at, and sink metadata.
{
"name": "high-volume-customers",
"description": "Customers with more than 100 events",
"query": {
"database": "default",
"sql": "SELECT customer_id, count() AS total FROM events GROUP BY customer_id HAVING total > 100"
},
"schedule": {
"cron": "*/5 * * * *",
"enabled": true
},
"sinks": []
}The required query object contains database and sql. Responses use the same
shape. Top-level database and sql fields are rejected.
schedule controls automatic executions. Set it to null for a manual-only
workflow. When omitted, it defaults to running every second:
{"cron": "* * * * * *", "enabled": true}. Within schedule, cron is
required and enabled defaults to true. sinks defaults to an empty list.
cron uses UTC. It accepts five fields (minute hour day-of-month month day-of-week) or six fields with second first. Each field accepts *, a
number, a range such as 9-17, a step such as */15 or 5/10, or a
comma-separated list.
Day of week uses 0–7; 0 and 7 mean Sunday. If both day fields are
restricted, a run requires both to match. Names, shortcuts such as @hourly,
and ?, L, or # return 422. The workflow service normalizes spaces and limits the
normalized expression to 1,024 characters. See schedule a workflow
for examples and missed-run behavior.
Each cluster supports up to 100 workflows, including paused workflows. Names are
trimmed and lowercased and must be unique in the cluster. The optional
description is trimmed, accepts up to 1,000 characters, and is null when
omitted or blank.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
name | body | string | Yes | — |
description | body | string or null | No | Optional free-form text. Surrounding whitespace is trimmed and blank values are stored as null. |
query | body | WorkflowQuery | Yes | — |
schedule | body | null or WorkflowSchedule | No | — |
sinks | body | array of SinkInput | No | — |
Request:
# Create a scheduled SQL workflow.
curl -X POST "https://api.rawtree.com/v1/workflows?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "event-count",
"query": {
"database": "analytics",
"sql": "SELECT count() AS total FROM events"
},
"schedule": null,
"sinks": []
}'Expected response: HTTP 201.
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "event-count",
"query": {
"database": "analytics",
"sql": "SELECT count() AS total FROM events"
},
"schedule": null,
"sinks": [],
"revision": 1,
"created_at": "2026-11-09T10:00:00Z",
"updated_at": "2026-11-09T10:00:00Z"
}Errors: 400, 403, 409, 422, 503. See API errors.
GET /v1/workflows/{workflow_id}
Get a workflow and its schedule state. HTTP sink responses include settings.url
and settings.header_names. The URL is null if its encrypted configuration
cannot be read; the workflow and sink metadata remain available for repair.
Header values are write-only and are never returned.
schedule is null for manual-only workflows. Otherwise it contains cron,
enabled, and next_run_at, the next scheduled evaluation time in
RFC 3339 format. next_run_at is null while paused or when no future time is
available. A successful create or update can also return null if schedule
metadata could not be refreshed.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
Request:
# Get a workflow and its sink configuration.
curl -X GET "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 200.
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "event-count",
"query": {
"database": "analytics",
"sql": "SELECT count() AS total FROM events"
},
"schedule": null,
"sinks": [],
"revision": 1,
"created_at": "2026-11-09T10:00:00Z",
"updated_at": "2026-11-09T10:00:00Z"
}Errors: 403, 404, 503. See API errors.
PATCH /v1/workflows/{workflow_id}
Update the supplied fields and return the saved workflow. Omitted fields retain
their existing values.
Omitting query preserves the saved query. Within query, omitted database
or sql fields retain their values; for example, {"query":{"sql":"SELECT 1"}}
changes only the SQL.
Within schedule, omitted fields retain their values:
{"schedule": {"enabled": false}} pauses automatic runs and
{"schedule": {"enabled": true}} resumes them and keeps the cron. Set
schedule to null for manual-only execution. To schedule it again, supply a
cron; enabled then defaults to true. Pausing does not prevent explicit runs.
Set description to null or blank text to clear it.
{ "schedule": { "cron": "*/10 * * * * *", "enabled": false } }Supplying sinks replaces the full list. Preserve existing sink IDs when editing;
omit id for new sinks. An empty list removes every sink. Each sink has a type
and a typed settings object: HTTP settings accept url and headers; table
settings require database and table; Slack settings require
integration_id and exactly one of channel_id or channel_name. HTTP
responses expose settings.url, settings.url_configured, and
settings.header_names, never header values. Slack responses expose
settings.integration_id, settings.channel_id, and settings.channel_name.
Flattened fields are rejected.
See sink configuration
for HTTP credentials and table sinks, and
Post results to Slack
for connecting a Slack workspace.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
name | body | string or null | No | — |
description | body | string or null | No | Omit to preserve the description; null or blank text clears it. |
query | body | null or UpdateWorkflowQuery | No | — |
schedule | body | null or UpdateWorkflowSchedule | No | — |
sinks | body | array or null | No | — |
Request:
# Update a workflow, including its schedule and sinks.
curl -X PATCH "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schedule": {
"cron": "*/5 * * * *",
"enabled": true
}
}'Expected response: HTTP 200.
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "event-count",
"query": {
"database": "analytics",
"sql": "SELECT count() AS total FROM events"
},
"schedule": {
"cron": "*/5 * * * *",
"enabled": true
},
"sinks": [],
"revision": 2,
"created_at": "2026-11-09T10:00:00Z",
"updated_at": "2026-11-09T10:00:00Z"
}Errors: 400, 403, 404, 409, 422, 503. See API errors.
DELETE /v1/workflows/{workflow_id}
Delete the workflow and its schedule. Returns 204 with no response body.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
Request:
# Delete a workflow and its schedule.
curl -X DELETE "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 204.
The response body is empty.
Errors: 403, 404, 503. See API errors.
POST /v1/workflows/{workflow_id}/sinks
Add one sink without resending the others. The body is a sink as in the
sinks list, without id; the new sink goes last. Returns 201 with the saved
sink. A workflow holds up to five sinks.
{ "type": "table", "settings": { "database": "analytics", "table": "alerts" } }Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
Request:
# Add a sink to a workflow.
curl -X POST "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/sinks?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "table",
"settings": {
"database": "analytics",
"table": "event_counts"
}
}'Expected response: HTTP 201.
{
"id": "11111111-1111-4111-8111-111111111111",
"type": "table",
"settings": {
"database": "analytics",
"table": "event_counts"
}
}Errors: 400, 403, 404, 409, 422, 503. See API errors.
PATCH /v1/workflows/{workflow_id}/sinks/{sink_id}
Change one sink and return it. Send the settings to change; omitted settings
keep their stored values. HTTP headers work as in a PATCH of sinks: the
map lists every header the sink keeps, null keeps a header's stored value,
and omitted headers are removed. type is optional and must match the sink's
type; to change the type, delete the sink and add a new one.
This keeps Authorization, adds X-Region, and removes any other header:
{ "settings": { "headers": { "Authorization": null, "X-Region": "eu" } } }Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
type | body | null or SinkType | No | — |
settings | body | HttpSinkSettingsInput or TableSinkSettingsPatch or SlackSinkSettingsPatch | Yes | — |
Request:
# Change one sink of a workflow; omitted settings keep their stored values.
curl -X PATCH "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/sinks/11111111-1111-4111-8111-111111111111?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"settings": {
"table": "event_counts_archive"
}
}'Expected response: HTTP 200.
{
"id": "11111111-1111-4111-8111-111111111111",
"type": "table",
"settings": {
"database": "analytics",
"table": "event_counts_archive"
}
}Errors: 400, 403, 404, 409, 422, 503. See API errors.
DELETE /v1/workflows/{workflow_id}/sinks/{sink_id}
Remove one sink. Returns 204 with no response body.
Sink changes through these routes stop queued and active runs of the previous
definition, as a PATCH of sinks does. Each route changes only its own sink,
so other sinks stay as saved. A sink PATCH returns 409 if the workflow
changes while it is applied.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
Request:
# Remove one sink from a workflow.
curl -X DELETE "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/sinks/11111111-1111-4111-8111-111111111111?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 204.
The response body is empty.
Errors: 403, 404, 409, 503. See API errors.
POST /v1/workflows/{workflow_id}/runs
Start an execution using the saved definition without changing its schedule.
Requires an organization administrator or admin API key. No request body is
required. Returns 202 with id, workflow_id, query_id, and
source: "manual".
The optional Idempotency-Key header accepts 1–128 visible ASCII characters
without spaces. Reusing a key recovers the same execution while Temporal retains
it. See explicit runs for
revision checks, overlap, and delivery semantics.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
Idempotency-Key | header | string or null | No | Optional retry key, 1–128 visible ASCII characters without spaces. Scoped to this workflow; deduplication lasts while Temporal retains the execution. |
Request:
# Execute a saved workflow once, independently of its schedule.
curl -X POST "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/runs?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 202.
{
"id": "11111111-1111-4111-8111-111111111111",
"workflow_id": "11111111-1111-4111-8111-111111111111",
"query_id": "workflow-docs-run",
"source": "manual"
}Errors: 400, 401, 403, 404, 503. See API errors.
POST /v1/workflows/{workflow_id}/runs/{run_id}/cancel
Request cancellation of one manual or scheduled execution. Requires an
organization administrator or admin API key, with the same organization and
cluster query parameters as the other workflow endpoints. No body is required.
Returns 202 with an empty body once cancellation is accepted.
curl -X POST \
"$RAWTREE_URL/v1/workflows/$WORKFLOW_ID/runs/$RUN_ID/cancel?organization=$ORGANIZATION&cluster=$CLUSTER" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Cancellation is asynchronous and requires a worker to process it. Use the runs list to inspect the eventual status. It preserves execution history, other concurrent runs, and future scheduled executions. It does not roll back completed writes or recall deliveries already handed off to sinks. Query cleanup can take time, especially when a database replica is unavailable.
Repeating cancellation is safe: a pending request or an already canceled run
returns 202. A run that has otherwise finished returns 409. Unknown runs,
runs belonging to another workflow, and deleted workflows return 404. Because
run lookup uses Temporal visibility, a newly started run may briefly return
404; retry once it appears in the runs list. A 503 means acceptance could not
be confirmed; retry cancellation for the same run.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Request:
# Request cancellation of one manual or scheduled run without changing its schedule.
# No body is required. Acceptance is asynchronous and does not undo completed writes.
# Recently started runs may not yet be visible. Repeating a request is safe;
# already canceled runs return 202 and other finished runs return 409.
curl -X POST "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/runs/11111111-1111-4111-8111-111111111111/cancel?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 202.
The response body is empty.
Errors: 400, 401, 403, 404, 409, 503. See API errors.
GET /v1/workflows/{workflow_id}/runs
List manual and scheduled executions retained by Temporal for this workflow.
limit defaults to 20 and accepts 1–100. This is a per-page limit, not a total
history limit. Pass next_cursor as cursor to request another page for the same
workflow. The page size may change between requests. next_cursor: null means
there are no more pages; an empty page with a cursor can still be continued.
{
"runs": [
{
"id": "01a10ffc-b4f7-7871-859c-6353f93d875f",
"workflow_id": "d668a6c1-1904-4a27-b113-ef54b5435483",
"query_id": "workflow:d668a6c1-1904-4a27-b113-ef54b5435483:01a10ffc-b4f7-7871-859c-6353f93d875f",
"source": "manual",
"status": "running",
"started_at": "2026-10-06T10:32:00Z",
"finished_at": null,
"deliveries": [
{
"sink_id": "5b0f2f0e-7c8e-4d4b-9a2b-6f1d0c3e8a71",
"status": "delivering"
}
]
}
],
"next_cursor": null
}id is the Temporal run ID and matches the ID returned by POST. query_id is
the RawTree query ID for the run, in the form
workflow:{workflow_id}:{run_id}. Search system.query_log for this ID to
inspect the query when it has executed. source is manual or scheduled.
status is running, completed, failed, canceled, terminated,
continued_as_new, timed_out, or paused. Timestamps use RFC 3339 in UTC;
finished_at is null until the execution closes.
Active runs appear first by descending start time, then finished runs by
descending finish time. Temporal visibility is eventually consistent: recent
starts and status changes may appear after a delay. Pagination reflects live
history, so refresh from the first page to see current state. Runs remain
available after pausing or removing a schedule, until Temporal expires their
history. Deleting the saved workflow makes its runs endpoint return 404.
Sinks receive rows asynchronously, and delivery to a sink is retried for up to an
hour. A run stays running until every sink it sent rows to reports how
delivery ended. It is completed only when every sink received its rows, and
failed if a sink rejected them or kept failing for an hour. If a sink's
outcome is still unknown when the 65-minute delivery confirmation window closes,
the run fails: the rows may have reached the sink, but the service could not confirm
it. Cancelling a run that
is waiting for its sinks closes it as canceled; rows already handed off may
still be delivered.
deliveries lists each sink the run sent rows to, ordered by sink ID, and is
empty when the run sent none. Each status is delivering, delivered,
failed, or delivery_unknown. These statuses describe this run's delivery, not
the sink's configuration. Inspect the workflow logs for SQL outcomes and each
delivery attempt. Invalid limits or cursors return 400; an unavailable
execution service returns 503.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Request:
# List manual and scheduled executions retained by Temporal.
# Active executions come first, then finished executions by descending finish
# time. Visibility is eventually consistent. A run stays `running` until every
# sink it handed rows to reports its delivery, and fails if any delivery
# failed or is unconfirmed after 65 minutes. `deliveries` shows each sink's
# progress; per-attempt details are in logs.
curl -X GET "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/runs?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 200.
{
"runs": [],
"next_cursor": null
}Errors: 400, 401, 403, 404, 503. See API errors.
GET /v1/workflows/{workflow_id}/logs
List execution and delivery history. Optional from (inclusive) and to
(exclusive) are Unix milliseconds and default to the last 24 hours. Ranges can
cover up to seven days. limit defaults to 100 and accepts 1–200.
The response contains logs, from, to, and next_cursor. Each log includes
event_type (for example, workflow.evaluated) and workflow_revision.
Delivery records identify the sink with sink_id and sink_type.
Pass the returned from, to, and next_cursor as cursor to fetch older logs
within the same window. History is best-effort observability data.
Use an API key, a permitted workload token, or a user bearer token. Database-role keys support only the operations listed in Authentication.
Parameters:
| Field | Location | Type | Required | Notes |
|---|---|---|---|---|
organization | query | string | Yes | — |
cluster | query | string | Yes | — |
from | query | integer or null | No | Inclusive Unix milliseconds. Defaults to the last 24 hours. |
to | query | integer or null | No | Exclusive Unix milliseconds. |
cursor | query | string or null | No | — |
limit | query | integer or null | No | — |
Request:
# List workflow execution and sink delivery logs.
curl -X GET "https://api.rawtree.com/v1/workflows/11111111-1111-4111-8111-111111111111/logs?organization=team_alpha&cluster=production" \
-H "Authorization: Bearer $RAWTREE_API_KEY"Expected response: HTTP 200.
{
"logs": [],
"from": 1794214800000,
"to": 1794218400000
}Errors: 400, 403, 404, 503. See API errors.
Sink delivery metrics
Read sink delivery metrics through GET /v1/metrics.
Each observation contains the organization, cluster, workflow
(rawtree.workflow.id), and sink (rawtree.workflow.sink.id) identities.
| Metric | Meaning | Unit |
|---|---|---|
rawtree.workflow.sink.errors | Terminal delivery failures; retry attempts are excluded | {error} |
rawtree.workflow.sink.written_rows | Rows in successful sink deliveries, including HTTP sinks | {row} |
These are monotonic delta sums. Use aggregation=sum to add deliveries
within each bucket. Do not use function=increase, which requires cumulative
counters. Filter by workflow ID and group by sink ID:
curl -X GET -G "https://api.rawtree.com/v1/metrics" \
-H "Authorization: Bearer $RAWTREE_SESSION_TOKEN" \
--data-urlencode "organization=$RAWTREE_ORGANIZATION" \
--data-urlencode "cluster=$RAWTREE_CLUSTER" \
--data-urlencode 'metric=rawtree.workflow.sink.errors,rawtree.workflow.sink.written_rows' \
--data-urlencode "filters={\"rawtree.workflow.id\":\"$WORKFLOW_ID\"}" \
--data-urlencode 'aggregation=sum' \
--data-urlencode 'granularity_seconds=60' \
--data-urlencode 'group_by=rawtree.workflow.sink.id' \
--data-urlencode 'window_seconds=3600'Metric export is buffered and best effort; it does not block sink delivery.
History starts when the deployment begins emitting these metrics. Existing logs
are not backfilled into metrics. Workflow logs remain available for individual
attempts, failure details, and links to runs. The sink metrics endpoint
/v1/workflows/{workflow_id}/sinks/metrics has been removed.