Trigger a pipeline run via the API
Supaflow pipelines can run on their own schedule, but you can also trigger a run from any external system - an orchestrator such as Airflow or Azure Data Factory, a CI job, or a plain cron entry. All you need is an API key and curl.
Triggering a run takes two HTTP calls:
- Authenticate - exchange your API key for a short-lived access token.
- Trigger - start the pipeline run and get back a job ID.
A third call lets you poll that job until it finishes, so the caller can block on the result instead of firing and forgetting.
Prerequisites
- An API key that starts with
ak_- see Get an API key below. Treat it as a secret: store it in your orchestrator's secret manager (Azure Key Vault, AWS Secrets Manager, GitHub Actions secrets, etc.), never inline in a committed script. - The pipeline ID you want to run (a UUID). See Find your pipeline ID below.
curlandjq(used here to parse JSON responses).
Get an API key
API requests are authenticated with an organization-scoped API key. The key value starts with ak_.
- In the Supaflow app, open Settings -> API Keys.
- Click Add new key, give it a descriptive name (for example,
Airflow triggerorADF trigger), and create it. - Copy the key value. It starts with
ak_and is shown only once - store it in your secret manager immediately. If you lose it, create a new key.
Keys are scoped to the organization (workspace) they are created in, so a key can only trigger and read pipelines in that workspace. Manage or revoke keys from the same page.
This flow requires an ak_... key from the main API Keys page. The separate Legacy API Keys section issues a different, session-style token that the authentication step below does not accept.
Step 1: Authenticate
Exchange your API key for a short-lived access token. The response also returns the API host and public key for your account's region, so you never have to hardcode region-specific URLs.
curl -sS -X POST https://app.supa-flow.io/api/cli/bootstrap \
-H "Authorization: Bearer ak_XXXXXXXXXXXXXXXXXXXX"
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"supabase_url": "https://<region-host>.supabase.co",
"supabase_anon_key": "eyJhbGciOi...",
"region": "us",
"ttl_seconds": 3600,
"expires_at": "2026-06-18T12:00:00.000Z"
}
You need three values from this response for the next calls:
| Field | Used as |
|---|---|
token | The Authorization: Bearer token for all subsequent calls. |
supabase_url | The API host to call. |
supabase_anon_key | The public apikey header value. |
The token is valid for one hour (ttl_seconds: 3600). For a single trigger this is plenty; for long-running polls, re-run this step when the token expires (the full script handles this automatically).
Step 2: Trigger the pipeline run
Call the create_pipeline_run_job operation with the token and host from step 1. It queues the run and returns the new job ID.
curl -sS -X POST "https://<region-host>.supabase.co/rest/v1/rpc/create_pipeline_run_job" \
-H "apikey: <supabase_anon_key>" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"p_pipeline_id": "11111111-2222-3333-4444-555555555555",
"p_job_type": "pipeline_run",
"p_reset_target": false,
"p_full_resync": false
}'
The response body is the new job ID as a JSON string:
"e1b2c3d4-5678-90ab-cdef-1234567890ab"
A response of null means the run was not created because the account is out of sync credits. Invalid, inactive, or inaccessible pipelines return an error instead.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
p_pipeline_id | UUID (required) | - | The pipeline to run. |
p_job_type | string | pipeline_run | Only pipeline_run is supported. |
p_full_resync | boolean | false | Reset cursors and re-ingest all data from scratch instead of an incremental sync. |
p_reset_target | boolean | false | Drop and recreate the destination tables before loading. Use only together with p_full_resync: true. |
For a normal incremental sync, leave p_full_resync and p_reset_target as false.
This call is asynchronous: it enqueues the run and returns immediately. The Supaflow agent picks the job up and executes it. To wait for the result, poll the job (step 3).
Step 3: Poll for completion
Read the job's status with the same token and host. Filter by the job ID returned in step 2.
curl -sS "https://<region-host>.supabase.co/rest/v1/jobs?id=eq.e1b2c3d4-5678-90ab-cdef-1234567890ab&select=id,job_status,status_message" \
-H "apikey: <supabase_anon_key>" \
-H "Authorization: Bearer <token>"
Response (an array with the single matching job):
[
{
"id": "e1b2c3d4-5678-90ab-cdef-1234567890ab",
"job_status": "running",
"status_message": null
}
]
Poll on an interval until job_status reaches a terminal state. Poll no more often than every 30 seconds - syncs run for minutes, so a tighter interval just adds load without getting you an answer sooner.
| Terminal status | Meaning |
|---|---|
completed | The run finished successfully. |
completed_with_warning | The run finished, but one or more objects had non-fatal issues. |
failed | The run failed. |
cancelled | The run was cancelled. |
timed_out | The run exceeded its time limit. |
skipped | The run was skipped (for example, nothing to do). |
Any other value (for example queued, picked, or running) means the job is still in progress - keep polling. A freshly triggered run starts as queued until an agent picks it up.
Full example: trigger and wait
A self-contained script that authenticates, triggers the run, and polls until the job finishes. It re-authenticates automatically if the one-hour token expires mid-sync, so it is safe for long-running pipelines.
#!/usr/bin/env bash
set -euo pipefail
# ---- configuration (set these as environment variables) ----
SUPAFLOW_API_KEY="${SUPAFLOW_API_KEY:?set SUPAFLOW_API_KEY (your ak_... key)}"
PIPELINE_ID="${PIPELINE_ID:?set PIPELINE_ID (the pipeline UUID)}"
APP_URL="${SUPAFLOW_APP_URL:-https://app.supa-flow.io}"
POLL_INTERVAL="${POLL_INTERVAL:-30}" # seconds between status checks (30s recommended minimum)
# ---- step 1: authenticate (sets JWT / SB_URL / SB_ANON) ----
authenticate() {
local resp
resp=$(curl -sS -X POST "$APP_URL/api/cli/bootstrap" \
-H "Authorization: Bearer $SUPAFLOW_API_KEY")
JWT=$(jq -r .token <<<"$resp")
SB_URL=$(jq -r .supabase_url <<<"$resp")
SB_ANON=$(jq -r .supabase_anon_key <<<"$resp")
if [[ -z "$JWT" || "$JWT" == "null" ]]; then
echo "authentication failed: $resp" >&2
exit 1
fi
}
authenticate
# ---- step 2: trigger the run ----
JOB_ID=$(curl -sS -X POST "$SB_URL/rest/v1/rpc/create_pipeline_run_job" \
-H "apikey: $SB_ANON" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d "{\"p_pipeline_id\":\"$PIPELINE_ID\",\"p_job_type\":\"pipeline_run\",\"p_reset_target\":false,\"p_full_resync\":false}" \
| jq -r .)
if [[ -z "$JOB_ID" || "$JOB_ID" == "null" ]]; then
echo "pipeline run was not created (out of sync credits)" >&2
exit 2
fi
echo "Triggered job: $JOB_ID"
# ---- step 3: poll until terminal ----
TERMINAL="completed completed_with_warning failed cancelled timed_out skipped"
while true; do
# capture the HTTP code so we can re-authenticate if the 1h token expired
http=$(curl -sS -o /tmp/sf_job.json -w '%{http_code}' \
"$SB_URL/rest/v1/jobs?id=eq.$JOB_ID&select=id,job_status,status_message" \
-H "apikey: $SB_ANON" \
-H "Authorization: Bearer $JWT")
if [[ "$http" == "401" ]]; then
authenticate # token expired -> get a fresh one and retry
continue
fi
STATUS=$(jq -r '.[0].job_status // "unknown"' /tmp/sf_job.json)
MESSAGE=$(jq -r '.[0].status_message // empty' /tmp/sf_job.json)
echo " status: $STATUS${MESSAGE:+ ($MESSAGE)}"
if [[ " $TERMINAL " == *" $STATUS "* ]]; then
break
fi
sleep "$POLL_INTERVAL"
done
echo "Final status: $STATUS"
case "$STATUS" in
completed|completed_with_warning) exit 0 ;;
*) exit 1 ;;
esac
Run it:
export SUPAFLOW_API_KEY="ak_XXXXXXXXXXXXXXXXXXXX"
export PIPELINE_ID="11111111-2222-3333-4444-555555555555"
./trigger-pipeline.sh
The script exits 0 when the pipeline completes (including completed_with_warning) and non-zero otherwise, so an orchestrator can branch on it directly.
Cancel a running job
To cancel a job that is still in progress, set its job_status to cancelled. The agent watches for this change and aborts the run on its next heartbeat; the job then settles in the terminal cancelled state.
Include a status precondition in the filter - job_status=in.(queued,picked,running) - so the update only applies while the job is still in progress. Without it, a race against completion (or a stale job ID) could overwrite a job that has already completed or failed.
curl -sS -X PATCH "https://<region-host>.supabase.co/rest/v1/jobs?id=eq.e1b2c3d4-5678-90ab-cdef-1234567890ab&job_status=in.(queued,picked,running)&select=id,job_status,status_message" \
-H "apikey: <supabase_anon_key>" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"job_status": "cancelled"}'
Prefer: return=representation makes the response an array of the rows that were actually updated, so you can confirm the cancel took effect:
[{ ... "job_status": "cancelled" ... }]- the job was in progress and is now cancelled.[](empty array) - nothing was changed: the job had already reached a terminal state, or the ID does not match. Re-read the job's status to see where it ended up.
Notes:
- Always check the affected rows (the array above) instead of assuming the cancel took effect. An empty array means the precondition did not match - the job had already finished.
- Cancellation is not instant. The agent acts on its next heartbeat, so a running job can take a few seconds to actually stop. Keep polling until
job_statusreadscancelled.
Find your pipeline ID
Each pipeline has a UUID. You can read it from the app URL when a pipeline is open, or fetch it programmatically.
With the Supaflow CLI:
supaflow pipelines list --json
Or with curl, using the token and host from step 1:
curl -sS "https://<region-host>.supabase.co/rest/v1/pipelines_and_datasources?select=pipeline_id,pipeline_name,pipeline_api_name" \
-H "apikey: <supabase_anon_key>" \
-H "Authorization: Bearer <token>"
Notes and limits
- Token lifetime. The access token from step 1 lasts one hour. Always authenticate immediately before triggering; for long polls, re-authenticate when a call returns HTTP
401(the full script does this). - Regions. Step 1 returns the correct host and key for your account's region, so the same script works for US, EU, and other regions without changes.
- Scope. Each call is scoped to the workspace that owns the API key. You can only trigger and read pipelines and jobs in that workspace.
- Idempotency. Each trigger call queues one run. Calling it twice queues two runs. If your orchestrator retries on network errors, guard against duplicate triggers (for example, only retry when you did not receive a job ID back).
Related pages
- API Reference overview - authentication and available operations
- API Keys - create and manage API keys
- Schedules - run pipelines on a recurring schedule inside Supaflow
- CLI - the
supaflowcommand-line tool
Support
Questions about the Supaflow API? Contact us at support@supa-flow.io.