Executing Workflows

Submit a workflow graph for execution, then query its status and results. Graph Compose returns a workflow ID immediately and processes the graph asynchronously.

API endpoints

All endpoints are relative to https://api.graphcompose.io/api/v1.

EndpointMethodDescription
/workflowsPOSTExecute a workflow asynchronously
/workflows/execute-syncPOSTExecute and wait for the final result
/workflows/validatePOSTValidate a workflow graph without executing
/workflows/{id}GETGet status, execution state, and node results
/workflows/{id}/terminatePOSTTerminate a running workflow

Execute a workflow

View full schema in API Reference →

Define your nodes, then call graph.execute(). The method returns immediately with a workflow ID and a status of RUNNING.

import { GraphCompose } from '@graph-compose/client'

const graph = new GraphCompose({
  token: process.env.GRAPH_COMPOSE_TOKEN
})

graph
  .node('get_order')
    .get('https://api.example.com/orders/{{ context.orderId }}')
  .end()

  .node('process_order')
    .post('https://api.example.com/orders/process')
    .withBody({
      items: '{{ results.get_order.data.items }}'
    })
    .withDependencies(['get_order'])
  .end()

const result = await graph.execute({
  context: { orderId: 'order_123' }
})

console.log('Workflow ID:', result.data.workflowId)
console.log('Status:', result.data.status) // "RUNNING"

The execute() method accepts an optional context object and an optional webhookUrl. Context values are available to all nodes via {{ context.key }} template expressions. See Webhooks for completion notifications.

Get workflow status and results

View full schema in API Reference →

GET /workflows/{id} returns the complete workflow state in a single response: metadata, status, and the full execution results for every node that has completed.

There is no separate "get state" endpoint. This one call gives you everything.

const workflow = await graph.getWorkflowStatus('wf_abc123')

if (workflow.success && workflow.data) {
  console.log('Status:', workflow.data.status)

  // executionState contains context, executed node list, and results
  const state = workflow.data.executionState
  console.log('Executed nodes:', state.executed)
  console.log('Order data:', state.results.get_order.data)
}

Understand the response

View response schemas in API Reference →

Every successful response follows this envelope:

Success envelope

{
  "success": true,
  "message": "Human-readable status message.",
  "data": { ... }
}

Error responses use a different shape. They never appear inside a 200 response.

Error envelope (4xx / 5xx)

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Workflow not found."
  }
}

Workflow data fields

The data object returned by GET /workflows/{id} contains:

FieldTypeDescription
workflowIdstringUnique identifier for the workflow.
runIdstringIdentifier for the specific execution run.
startedAtstringISO 8601 timestamp when execution began.
userIdstringID of the user who started the workflow.
organizationIdstringID of the organization that owns the workflow.
statusstringCurrent execution status. See status values.
executionStateobjectContext, executed node list, and per-node results. See below.
eventsarrayTemporal execution history. Only included when ?includeEvents=true.
descriptionobjectTemporal workflow metadata. Only included when ?includeEvents=true.

Execution state fields

The executionState object is the core of every workflow response.

FieldTypeDescription
contextRecord<string, unknown>The input context you passed at execution time, merged with any updates made during the run.
executedstring[]Ordered list of node IDs that have completed.
resultsRecord<string, NodeResult>Map of node ID to its result. See node result structure.

Node result structure

View full schema in API Reference →

Each entry in executionState.results has this shape:

FieldTypeDescription
dataobject | string | nullThe parsed HTTP response body. JSON responses are parsed into objects. Text responses are returned as strings. Empty bodies are null.
statusCodenumberHTTP status code returned by the endpoint (e.g. 200, 404).
headersRecord<string, string>Response headers as key-value pairs.

Example for a single node:

Node result example

{
  "get_order": {
    "data": {
      "id": "order_123",
      "items": ["item_a", "item_b"],
      "total": 59.99
    },
    "statusCode": 200,
    "headers": {
      "content-type": "application/json"
    }
  }
}

Reference these values in downstream nodes with template expressions:

{{ results.get_order.data.total }}        → 59.99
{{ results.get_order.statusCode }}        → 200
{{ results.get_order.data.items[0] }}     → "item_a"

See Template Syntax for the full expression reference.

Workflow status values

StatusDescription
RUNNINGThe workflow is currently executing.
COMPLETEDAll nodes finished successfully.
FAILEDOne or more nodes failed and the workflow could not recover.
CANCELLEDThe workflow was cancelled before completion.
TERMINATEDThe workflow was terminated via the API.
CONTINUED_AS_NEWThe workflow restarted with a new run. Used internally by long-running workflows.
TIMED_OUTThe workflow exceeded its maximum allowed duration.

Poll until complete

If you do not use webhooks, poll GET /workflows/{id} until the status is no longer RUNNING.

const workflowId = result.data.workflowId
let status = 'RUNNING'

while (status === 'RUNNING') {
  await new Promise(resolve => setTimeout(resolve, 2000))
  const check = await graph.getWorkflowStatus(workflowId)

  if (check.success && check.data) {
    status = check.data.status
  }
}

// Fetch final results
const final = await graph.getWorkflowStatus(workflowId)
console.log('Final results:', final.data.executionState.results)

Terminate a workflow

View full schema in API Reference →

Call graph.terminateWorkflow(workflowId) to stop a running workflow. Pass an optional reason string for auditing. Terminated workflows cannot be resumed.

const result = await graph.terminateWorkflow('wf_abc123', {
  reason: 'Order cancelled by customer'
})

console.log('Status:', result.data.status) // "TERMINATED"

Webhooks

View webhook payload schema →

Pass a webhookUrl when executing a workflow to receive a POST notification when the workflow completes. This removes the need to poll.

const result = await graph.execute({
  context: { orderId: 'order_123' },
  webhookUrl: 'https://api.example.com/webhooks/workflow-complete'
})

Graph Compose sends a POST request to your webhook URL with the workflow result in the body when execution finishes. Webhook failures are logged but do not affect the workflow outcome.

Synchronous execution

View full schema in API Reference →

POST /workflows/execute-sync blocks until the workflow completes and returns the final result inline. The response body has the same shape as GET /workflows/{id}.

Synchronous execution

curl -X POST https://api.graphcompose.io/api/v1/workflows/execute-sync \
  -H "Authorization: Bearer $GRAPH_COMPOSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nodes": [
      {
        "id": "get_user",
        "type": "http",
        "http": {
          "method": "GET",
          "url": "https://api.example.com/users/{{ context.userId }}"
        }
      }
    ],
    "context": { "userId": "user_123" }
  }'

Next steps