Documentation Index
Fetch the complete documentation index at: https://docs.gp.scale.com/llms.txt
Use this file to discover all available pages before exploring further.
This tutorial shows how to run a Workflows workflow programmatically over HTTPS. You need an API key, your account ID, and a workflow that is already defined and saved in the Workflows UI. You do not need to manage the underlying infrastructure.
Prerequisites
- A workflow defined and saved in the Workflows UI
- A Scale API key
- Your Scale account ID (the account that owns the workflow)
If you are missing credentials, contact your Scale representative.
Authentication
Send requests over HTTPS to your SGP host—the same origin you use to open the Workflows UI. Every example below uses a base URL of the form https://<your-sgp-host>/corp-api/compass/api.
Include these headers on each request:
x-scale-api-key: <your-api-key>
x-selected-account-id: <your-account-id>
<your-api-key> is your Scale API key. <your-account-id> is the ID of the account that owns the workflow you want to run.
Run and wait
The usual pattern is to start a run, then block until it finishes (up to a configurable timeout).
const BASE_URL = 'https://<your-sgp-host>/corp-api/compass/api';
const HEADERS = {
'x-scale-api-key': process.env.SCALE_API_KEY!,
'x-selected-account-id': process.env.SCALE_ACCOUNT_ID!,
'Content-Type': 'application/json',
};
async function runWorkflow(workflowId: string, variables?: Record<string, string>) {
const runRes = await fetch(`${BASE_URL}/v1/workflow/run`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({ workflowId, variables }),
});
if (!runRes.ok) throw new Error(`Failed to start workflow: ${await runRes.text()}`);
const { sessionId } = await runRes.json();
const resultRes = await fetch(
`${BASE_URL}/v1/sessions/${sessionId}/result?timeout=600000`,
{ headers: HEADERS },
);
if (!resultRes.ok) throw new Error(`Failed to get result: ${await resultRes.text()}`);
return resultRes.json();
}
async function main() {
const result = await runWorkflow('wf-abc123', { region: 'US', date: '2024-01-01' });
console.log(`${result.data.length} rows returned`);
console.log(result.data);
}
main().catch(console.error);
curl equivalent
BASE_URL="https://<your-sgp-host>/corp-api/compass/api"
SESSION_ID=$(curl -s -X POST "$BASE_URL/v1/workflow/run" \
-H "x-scale-api-key: $SCALE_API_KEY" \
-H "x-selected-account-id: $SCALE_ACCOUNT_ID" \
-H "Content-Type: application/json" \
-d '{"workflowId": "wf-abc123", "variables": {"region": "US"}}' \
| jq -r '.sessionId')
curl -s "$BASE_URL/v1/sessions/$SESSION_ID/result?timeout=600000" \
-H "x-scale-api-key: $SCALE_API_KEY" \
-H "x-selected-account-id: $SCALE_ACCOUNT_ID"
API reference
All paths below are relative to BASE_URL (https://<your-sgp-host>/corp-api/compass/api).
POST /v1/workflow/run
Starts a workflow run and returns immediately with a sessionId.
Request body:
{
"workflowId": "wf-abc123",
"variables": {
"region": "US",
"threshold": "0.8"
}
}
variables is optional. Values override defaults configured in the workflow and are substituted into {{variable_name}} placeholders in your cards. For typical use cases, use flat string values.
Response:
{ "sessionId": "compass-sess-..." }
GET /v1/sessions/:sessionId
Returns the current status of a running session. Use this endpoint when you want to poll instead of blocking on the result endpoint.
Example response:
{
"sessionState": {
"status": "idle",
"cards": [
{
"id": "card-0",
"context": { "cardType": "import_csv" },
"status": "completed",
"output": {}
}
]
}
}
Session status values
| Status | Meaning |
|---|
starting | Session is initializing |
pending_work | Cards are queued to run |
processing | A card is currently executing |
idle | All cards finished; results are ready |
errored | The session hit an unrecoverable error |
inactive | Session expired or was cancelled |
Card status values
| Status | Meaning |
|---|
idle | Card has not been scheduled yet |
pending | Queued to run |
processing | Currently executing |
completed | Finished successfully |
errored | Failed; check errorMessage |
GET /v1/sessions/:sessionId/result?timeout=<ms>
Waits until the session reaches idle or errored, or until timeout milliseconds elapse (default 600000, ten minutes), then returns the final output.
Query parameters
timeout — milliseconds to wait (default 600000)
Example response:
{
"data": [
{ "name": "Alice", "score": 0.92 },
{ "name": "Bob", "score": 0.85 }
],
"schema": [
{ "id": "name", "header": "Name", "type": "string" },
{ "id": "score", "header": "Score", "type": "number" }
],
"start": 0,
"end": 2,
"result": {
"summary": {
"totalNumberOfRows": 2,
"executionDurationMillis": 3210
}
}
}
data contains the rows. schema describes the columns. start and end describe the row index range returned.
Polling instead of blocking
If you prefer short requests instead of a long-lived connection, poll GET /v1/sessions/:sessionId until the session is idle, then call GET /v1/sessions/:sessionId/result to fetch the tabular output.
The following example reuses the same BASE_URL and HEADERS as in Run and wait.
async function waitForResult(sessionId: string, pollIntervalMs = 5000, timeoutMs = 600000) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`${BASE_URL}/v1/sessions/${sessionId}`, { headers: HEADERS });
if (!res.ok) throw new Error(`[${res.status}] Failed to poll session: ${await res.text()}`);
const { sessionState } = await res.json();
if (sessionState.status === 'errored' || sessionState.status === 'inactive') {
throw new Error(`Session ended with status: ${sessionState.status}`);
}
if (sessionState.status === 'idle') {
const failedCards = sessionState.cards.filter((c: { status: string }) => c.status === 'errored');
if (failedCards.length > 0) {
throw new Error(
`${failedCards.length} card(s) failed: ${failedCards.map((c: { errorMessage?: string }) => c.errorMessage).join(', ')}`,
);
}
return sessionState;
}
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
}
throw new Error('Timed out waiting for session to complete');
}
Polling returns session state (status and card metadata), not the full workflow table. After the session reaches idle, call GET /v1/sessions/:sessionId/result to retrieve the rows.
Workflow variables
Variables let you parameterize a workflow without editing its definition. They replace {{variable_name}} placeholders in card configuration (for example, filter values, prompt templates, or model parameters).
await runWorkflow('wf-abc123', {
model: 'gpt-4o',
prompt: 'Summarize the following in one sentence',
min_score: '0.7',
});
Values you pass override defaults saved on the workflow. Any variable you omit uses the workflow’s saved default.
Error handling
Error bodies are JSON. Many errors look like this:
{
"error": "Workflow not found or you do not have access.",
"status_code": 404
}
Authentication failures may include both error and message:
{
"error": "Unauthorized",
"message": "Missing authentication header."
}
| HTTP status | Meaning |
|---|
| 400 | Malformed request (missing required field, bad format) |
| 401 | Missing or invalid API key or account ID |
| 404 | Workflow or session not found, or you do not have access |
| 500 | Internal server error |
This variant parses JSON error bodies from failed responses:
async function runWorkflowSafe(workflowId: string, variables?: Record<string, string>) {
const runRes = await fetch(`${BASE_URL}/v1/workflow/run`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({ workflowId, variables }),
});
if (!runRes.ok) {
const err = await runRes.json().catch(() => ({ error: runRes.statusText }));
throw new Error(`[${runRes.status}] ${err.error ?? err.message}`);
}
const { sessionId } = await runRes.json();
const resultRes = await fetch(
`${BASE_URL}/v1/sessions/${sessionId}/result?timeout=600000`,
{ headers: HEADERS },
);
if (!resultRes.ok) {
const err = await resultRes.json().catch(() => ({ error: resultRes.statusText }));
throw new Error(`[${resultRes.status}] ${err.error ?? err.message}`);
}
return resultRes.json();
}
runWorkflowSafe assumes BASE_URL and HEADERS are defined the same way as in Run and wait.