Vantage REST API
The Vantage REST API lets you programmatically manage workflows, execute them, retrieve results, and discover available node types. All endpoints live under /api/v1/.
Base URL
https://your-vantage-instance.com/api/v1
All requests must include an Authorization header with a valid API key.
Authentication
Every request to the v1 API must include an API key in the Authorization header using the Bearer scheme:
Authorization: Bearer vntg_abc123...API keys are created from the Vantage Settings page or via the /api/v1/keys endpoint. Each key has:
| Property | Description |
|---|---|
| name | A human-readable label for the key |
| scope_level | user (only your resources) or client (all resources in your organization) |
| scopes | Permission array: workflows:read, workflows:write, workflows:execute |
| expires_at | Optional expiration date (ISO 8601) |
Important: The full API key is shown only once when created. Store it securely — it cannot be retrieved again.
Rate Limiting
The API enforces a rate limit of 60 requests per minute per API key. If exceeded, you'll receive a 429 Too Many Requests response.
CORS
All v1 endpoints support CORS with Access-Control-Allow-Origin: *, so they can be called from browser-based applications.
Response Envelope
All successful responses follow this structure:
{
"success": true,
"data": { ... },
"meta": {
"request_id": "abc123xyz",
"timestamp": "2026-04-29T12:00:00.000Z"
}
}Error responses:
{
"success": false,
"error": {
"code": "WORKFLOW_NOT_FOUND",
"message": "Workflow 42 not found"
},
"meta": {
"request_id": "abc123xyz",
"timestamp": "2026-04-29T12:00:00.000Z"
}
}API Key Management
Create an API Key
POST /api/v1/keysCreate a new API key. The full key value is returned only in this response — store it immediately.
Request Body:
{
"name": "My Production Key",
"scope_level": "user",
"scopes": ["workflows:read", "workflows:execute"],
"expires_at": "2027-01-01T00:00:00.000Z"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✅ | Human-readable name (max 100 chars) |
scope_level | string | No | "user" (default) or "client" |
scopes | string[] | No | Permission array. Valid values: workflows:read, workflows:write, workflows:execute |
expires_at | string | No | ISO 8601 expiration date |
Example Response:
{
"success": true,
"key": {
"id": 12,
"name": "My Production Key",
"key_prefix": "vntg_abc1",
"scope_level": "user",
"scopes": ["workflows:read", "workflows:execute"],
"expires_at": "2027-01-01T00:00:00.000Z",
"created": "2026-04-29T12:00:00.000Z"
},
"full_key": "vntg_abc123def456ghi789jkl012mno345"
}List API Keys
GET /api/v1/keysReturns all API keys for your account. The key value itself is never returned — only the prefix for identification.
Example Response:
{
"success": true,
"keys": [
{
"id": 12,
"name": "My Production Key",
"key_prefix": "vntg_abc1",
"scope_level": "user",
"scopes": ["workflows:read", "workflows:execute"],
"is_active": true,
"last_used_at": "2026-04-29T11:30:00.000Z",
"expires_at": "2027-01-01T00:00:00.000Z",
"created": "2026-04-29T12:00:00.000Z"
}
]
}Update an API Key
PATCH /api/v1/keys/:keyIdUpdate a key's name or scopes. You cannot change the key value, scope level, or hash.
Request Body:
{
"name": "Renamed Key",
"scopes": ["workflows:read", "workflows:write", "workflows:execute"]
}Revoke an API Key
DELETE /api/v1/keys/:keyIdSoft-revokes a key (sets is_active = false). The key immediately stops working.
Example Response:
{
"success": true,
"revoked": true
}Workflows
List Workflows
GET /api/v1/workflowsReturns all workflows visible to your API key. Results are scoped by your key's scope_level:
user— workflows you own + workflows shared with your organizationclient— all workflows across the organization
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
page | integer | 0 | Page number (0-indexed) |
limit | integer | 50 | Results per page (max 100) |
Example Response:
{
"success": true,
"data": {
"workflows": [
{
"id": 42,
"title": "Daily Sales Report",
"description": "Pulls Stripe data and generates a summary",
"is_active": true,
"created": "2026-03-15T09:00:00.000Z",
"modified": "2026-04-28T14:30:00.000Z",
"node_count": 5,
"edge_count": 4
}
],
"total": 1,
"page": 0,
"limit": 50
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-04-29T12:00:00.000Z"
}
}Get a Workflow
GET /api/v1/workflows/:workflowIdReturns a single workflow with its full graph (nodes and edges).
Example Response:
{
"success": true,
"data": {
"id": 42,
"title": "Daily Sales Report",
"description": "Pulls Stripe data and generates a summary",
"is_active": true,
"created": "2026-03-15T09:00:00.000Z",
"modified": "2026-04-28T14:30:00.000Z",
"nodes": [
{
"id": 101,
"node_type": "stripe/listCharges",
"node_role": "getter",
"label": "List Charges",
"config": {
"limit": 100,
"status": "succeeded"
}
},
{
"id": 102,
"node_type": "queryoperators/computedColumn",
"node_role": "setter",
"label": "Calculate Revenue",
"config": {
"column_name": "revenue_usd",
"expression": "amount / 100"
}
},
{
"id": 103,
"node_type": "termination/dashboardOutput",
"node_role": "terminator",
"label": "Output to Dashboard",
"config": {}
}
],
"edges": [
{
"id": 201,
"source_node_id": 101,
"target_node_id": 102,
"source_key": "output1",
"target_key": "input1"
},
{
"id": 202,
"source_node_id": 102,
"target_node_id": 103,
"source_key": "output1",
"target_key": "input1"
}
]
},
"meta": {
"request_id": "req_def456",
"timestamp": "2026-04-29T12:00:00.000Z"
}
}Create a Workflow
POST /api/v1/workflowsRequest Body:
{
"title": "New Sales Pipeline",
"description": "Automated Stripe charge processing",
"nodes": [
{
"node_type": "stripe/listCharges",
"node_role": "getter",
"label": "List Charges",
"config": { "limit": 50 }
},
{
"node_type": "termination/dashboardOutput",
"node_role": "terminator",
"label": "Dashboard Output",
"config": {}
}
],
"edges": [
{
"source_node_index": 0,
"target_node_index": 1,
"source_key": "output1",
"target_key": "input1"
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
title | string | ✅ | Workflow name (max 255 chars) |
description | string | No | Description text |
nodes | array | No | Array of node definitions |
edges | array | No | Array of edge definitions (uses source_node_index / target_node_index to reference nodes by array position) |
Note: Edge indices are 0-based references to the nodes array position, not database IDs. The API resolves them to actual node IDs automatically.
Node Roles:
| Role | Description |
|---|---|
getter | Fetches data from a source (integration, database, API) |
setter | Transforms, filters, or enriches data |
terminator | Outputs the final result (dashboard, email, export, etc.) |
Every workflow must have at least one terminator node.
Update a Workflow
PATCH /api/v1/workflows/:workflowIdUpdate metadata, or replace the entire node/edge graph by including nodes in the body.
Metadata-only update:
{
"title": "Updated Title",
"description": "New description",
"is_active": false
}Full graph replacement:
{
"title": "Updated Pipeline",
"nodes": [
{ "node_type": "stripe/listCharges", "node_role": "getter", "label": "Charges", "config": {} },
{ "node_type": "queryoperators/aggregation", "node_role": "setter", "label": "Sum Revenue", "config": {} },
{ "node_type": "termination/dashboardOutput", "node_role": "terminator", "label": "Output", "config": {} }
],
"edges": [
{ "source_node_index": 0, "target_node_index": 1 },
{ "source_node_index": 1, "target_node_index": 2 }
]
}When nodes is included, the existing graph is atomically replaced — all previous nodes and edges are deleted and recreated.
The response includes a node_id_map that maps your 0-based node indices to the new database IDs:
{
"node_id_map": { "0": 301, "1": 302, "2": 303 }
}Delete a Workflow
DELETE /api/v1/workflows/:workflowIdPermanently deletes a workflow and all its nodes, edges, and schedules.
Returns 204 No Content on success.
Workflow Execution
Execute a Workflow
POST /api/v1/workflows/:workflowId/executeRuns a workflow and returns the results. Execution happens synchronously — the response is returned when the workflow completes.
Request Body (optional):
{
"params": {
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
}| Field | Type | Description |
|---|---|---|
params | object | Key-value parameters passed to the workflow's input nodes |
Example Response:
{
"success": true,
"data": {
"workflow_id": 42,
"status": "completed",
"execution_time_ms": 2340,
"result_id": "dataRef:wf42_1714392000_abc123",
"rows": [
{ "charge_id": "ch_1abc", "amount": 4999, "currency": "usd", "revenue_usd": 49.99 },
{ "charge_id": "ch_2def", "amount": 2500, "currency": "usd", "revenue_usd": 25.00 }
],
"total_rows": 2,
"schema": {
"charge_id": "string",
"amount": "number",
"currency": "string",
"revenue_usd": "number"
}
},
"meta": {
"request_id": "req_exec_789",
"timestamp": "2026-04-29T12:00:02.340Z"
}
}Use the result_id to paginate through large result sets without re-executing:
Validate a Workflow
POST /api/v1/workflows/:workflowId/validateChecks whether a workflow graph is valid without executing it. Returns validation errors if any nodes are misconfigured or edges are invalid.
Example Response (valid):
{
"success": true,
"data": {
"valid": true,
"warnings": []
}
}Example Response (invalid):
{
"success": true,
"data": {
"valid": false,
"errors": [
{ "node_id": 102, "message": "Missing required config field: column_name" },
{ "node_id": null, "message": "No terminator node found in workflow" }
]
}
}Execution Data
Get Execution Results
GET /api/v1/workflows/:workflowId/data/:resultIdPaginate through previously executed workflow results using the result_id from an execute response.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
page | integer | 0 | Page number (0-indexed) |
limit | integer | 100 | Rows per page (max 1000) |
Example Response:
{
"success": true,
"data": {
"rows": [
{ "charge_id": "ch_1abc", "amount": 4999, "currency": "usd" },
{ "charge_id": "ch_2def", "amount": 2500, "currency": "usd" }
],
"total_rows": 248,
"schema": {
"charge_id": "string",
"amount": "number",
"currency": "string"
},
"page": 0,
"limit": 100,
"has_more": true,
"next_page_url": "/api/v1/workflows/42/data/dataRef:wf42_abc123?page=1&limit=100"
}
}Note: Execution results are stored temporarily and may expire. If a DATA_EXPIRED error is returned, re-execute the workflow.
Node Types
List Available Node Types
GET /api/v1/node-typesReturns all available workflow node types with their categories, roles, and configuration requirements.
Query Parameters:
| Param | Type | Description |
|---|---|---|
category | string | Filter by category name (case-insensitive, partial match) |
role | string | Filter by node role: getter, setter, terminator |
Example Request:
GET /api/v1/node-types?category=stripe&role=getterExample Response:
{
"success": true,
"data": {
"categories": [
{ "id": 15, "name": "Stripe", "node_count": 8 }
],
"node_types": [
{
"node_type": "stripe/listCharges",
"category": "Stripe",
"label": "List Charges",
"node_role": "getter",
"description": "Retrieve a list of charges from Stripe",
"inputs": ["input1"],
"outputs": ["output1"],
"requires_credential": true
},
{
"node_type": "stripe/getBalance",
"category": "Stripe",
"label": "Get Balance",
"node_role": "getter",
"description": "Retrieve the current Stripe account balance",
"inputs": ["input1"],
"outputs": ["output1"],
"requires_credential": true
}
],
"total": 2
}
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key |
FORBIDDEN | 403 | API key lacks the required scope |
RATE_LIMITED | 429 | Exceeded 60 requests/minute |
INVALID_INPUT | 400 | Request body validation failed |
INVALID_BODY | 400 | Request body is not valid JSON |
WORKFLOW_NOT_FOUND | 404 | Workflow doesn't exist or isn't accessible |
DATA_EXPIRED | 404 | Execution results have expired |
USER_NOT_FOUND | 404 | Authenticated user not found |
SERVICE_UNAVAILABLE | 503 | Backend service (e.g. data store) unavailable |
INTERNAL_ERROR | 500 | Unexpected server error |
Quick Start
1. Create an API Key
curl -X POST https://your-instance.com/api/v1/keys \
-H "Content-Type: application/json" \
-H "Cookie: your-session-cookie" \
-d '{
"name": "My First Key",
"scope_level": "user",
"scopes": ["workflows:read", "workflows:write", "workflows:execute"]
}'Save the full_key from the response — you'll need it for all subsequent requests.
2. List Your Workflows
curl https://your-instance.com/api/v1/workflows \
-H "Authorization: Bearer vntg_your_key_here"3. Execute a Workflow
curl -X POST https://your-instance.com/api/v1/workflows/42/execute \
-H "Authorization: Bearer vntg_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "params": { "date_range": "last_30_days" } }'4. Paginate Results
curl "https://your-instance.com/api/v1/workflows/42/data/dataRef:wf42_abc123?page=1&limit=50" \
-H "Authorization: Bearer vntg_your_key_here"5. Discover Node Types
curl "https://your-instance.com/api/v1/node-types?category=slack" \
-H "Authorization: Bearer vntg_your_key_here"SDK Examples
JavaScript / Node.js
const VANTAGE_API = 'https://your-instance.com/api/v1';
const API_KEY = 'vntg_your_key_here';
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
};
// List workflows
const workflows = await fetch(`${VANTAGE_API}/workflows`, { headers })
.then(r => r.json());
// Execute a workflow
const result = await fetch(`${VANTAGE_API}/workflows/42/execute`, {
method: 'POST',
headers,
body: JSON.stringify({ params: { limit: 100 } }),
}).then(r => r.json());
console.log(`Completed in ${result.data.execution_time_ms}ms`);
console.log(`${result.data.total_rows} rows returned`);Python
import requests
API_BASE = "https://your-instance.com/api/v1"
API_KEY = "vntg_your_key_here"
headers = {"Authorization": f"Bearer {API_KEY}"}
# List workflows
workflows = requests.get(f"{API_BASE}/workflows", headers=headers).json()
# Execute a workflow
result = requests.post(
f"{API_BASE}/workflows/42/execute",
headers=headers,
json={"params": {"date_range": "last_30_days"}}
).json()
# Paginate through results
result_id = result["data"]["result_id"]
page = 0
all_rows = []
while True:
data = requests.get(
f"{API_BASE}/workflows/42/data/{result_id}?page={page}&limit=100",
headers=headers
).json()
all_rows.extend(data["data"]["rows"])
if not data["data"]["has_more"]:
break
page += 1
print(f"Total rows: {len(all_rows)}")cURL — Create a Workflow via API
curl -X POST https://your-instance.com/api/v1/workflows \
-H "Authorization: Bearer vntg_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Slack Alert Pipeline",
"description": "Monitors Stripe charges and sends Slack alerts for high-value transactions",
"nodes": [
{
"node_type": "stripe/listCharges",
"node_role": "getter",
"label": "Get Recent Charges",
"config": { "limit": 50, "status": "succeeded" }
},
{
"node_type": "queryoperators/filter",
"node_role": "setter",
"label": "High Value Only",
"config": { "field": "amount", "operator": "gt", "value": 10000 }
},
{
"node_type": "slack/sendMessage",
"node_role": "terminator",
"label": "Alert Sales Team",
"config": { "channel": "#sales-alerts" }
}
],
"edges": [
{ "source_node_index": 0, "target_node_index": 1 },
{ "source_node_index": 1, "target_node_index": 2 }
]
}'