Skip to main content

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
StatusMeaning
startingSession is initializing
pending_workCards are queued to run
processingA card is currently executing
idleAll cards finished; results are ready
erroredThe session hit an unrecoverable error
inactiveSession expired or was cancelled
Card status values
StatusMeaning
idleCard has not been scheduled yet
pendingQueued to run
processingCurrently executing
completedFinished successfully
erroredFailed; 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 statusMeaning
400Malformed request (missing required field, bad format)
401Missing or invalid API key or account ID
404Workflow or session not found, or you do not have access
500Internal 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.