Actions
Execute and monitor units of work on your connections
Actions
Actions are discrete units of work that execute on connections. They represent the "verbs" of VirtuousAI — the things you want to do with your connected services.
Concept
An action combines:
- Template — What to do (e.g.,
dlt_extract,web_search) - Connection — Where to connect (credentials)
- Config — How to do it (parameters)
Examples:
| Template | Connection | Config |
|---|---|---|
dlt_extract | conn_shopify | { resources: ["orders"], incremental: true } |
web_search | conn_rest_api | { query: "customer data" } |
insight_delegate | — | { prompt: "Summarize..." } |
Action Types
Actions fall into categories based on what they do:
| Type | Description | Examples |
|---|---|---|
| Data Syncs | Pull data from external services into VirtuousAI | dlt_extract from Shopify, Salesforce |
| Transformations | Process and transform data | DuckDB queries, aggregations |
| Integrations | Push data to destination services | Export to warehouse, call APIs |
| AI Operations | LLM-powered operations | insight_delegate for summarization |
Action Lifecycle
| Status | Description | Can Transition To |
|---|---|---|
PENDING | Created, awaiting execution | RUNNING, CANCELLED, AWAITING_APPROVAL |
AWAITING_APPROVAL | Requires human approval | RUNNING (approved), REJECTED |
RUNNING | Actively executing | COMPLETED, FAILED, CANCELLED |
COMPLETED | Finished successfully | — (terminal) |
FAILED | Encountered an error | — (terminal, can retry) |
CANCELLED | Manually cancelled | — (terminal) |
REJECTED | Approval denied | — (terminal) |
Execution Model
VirtuousAI uses two execution modes:
| Mode | When Used | Characteristics |
|---|---|---|
| SYNC | Fast operations (under 30s) | Inline execution, immediate response |
| ASYNC_QUEUE | Long-running operations | SQS + Dramatiq workers, lease-based |
Lease-Based Ownership
For ASYNC_QUEUE operations:
- Lease Duration: 90 seconds
- Heartbeat Interval: Every 30 seconds
- Watchdog Grace: 180 seconds before marking abandoned
If a worker crashes, the watchdog detects the stale lease and marks the run as failed, preventing zombie jobs.
ActionRun Structure
Each execution creates an ActionRun — an immutable record:
{
"id": "run_xyz789",
"actionId": "action_abc123",
"status": "COMPLETED",
"startedAt": "2026-01-22T14:30:00Z",
"completedAt": "2026-01-22T14:32:15Z",
"result": {
"recordsProcessed": 1250,
"bytesTransferred": 2100000,
"tables": ["orders", "order_line_items"]
},
"artifacts": [
{ "name": "orders.parquet", "size": 1500000 }
]
}ActionRuns are immutable. Even if an action is deleted, historical runs are preserved for auditing.
Creating Actions
{
"kind": "dlt_extract",
"connectionRef": { "slug": "shopify" },
"definition": {
"source": "shopify",
"resources": ["orders", "products"],
"incremental": true,
"start_date": "2026-01-01"
}
}{
"kind": "insight_delegate",
"definition": {
"prompt": "Summarize the key trends in this quarter's sales data",
"context": "{{previous_step.output}}"
}
}{
"kind": "web_search",
"connectionRef": { "id": "conn_rest_prod" },
"definition": {
"method": "POST",
"url": "/api/customers",
"body": { "status": "active" }
}
}Connection Resolution
Actions resolve connections flexibly:
| Reference Type | Example | Resolution |
|---|---|---|
| By ID | { "id": "conn_abc123" } | Exact match |
| By Slug | { "slug": "shopify" } | LLM-friendly name lookup |
| By Provider | { "provider": "shopify" } | Uses org's default for provider |
Streaming Execution
For long-running actions, subscribe to real-time progress via Server-Sent Events:
GET /api/v1/action-runs/{run_id}/stream
Accept: text/event-streamEvents include:
progress— Percentage completelog— Execution log messagescomplete— Final resulterror— Failure details
Error Handling
When actions fail, the run includes detailed error information:
{
"status": "FAILED",
"error": {
"code": "CONNECTION_ERROR",
"message": "Failed to connect to Shopify API",
"details": {
"statusCode": 401,
"shopifyError": "Invalid API key"
},
"retryable": true
}
}Error Codes
| Code | Description | Retryable |
|---|---|---|
CONNECTION_ERROR | Failed to connect to external service | Usually yes |
AUTH_ERROR | Authentication/authorization failed | No (fix credentials) |
RATE_LIMITED | External API rate limit hit | Yes (with backoff) |
DATA_ERROR | Invalid or corrupt data | No (fix source data) |
TIMEOUT | Execution exceeded time limit | Sometimes |
INTERNAL_ERROR | Unexpected system error | Yes |
Retry Behavior
Retryable errors are automatically retried with exponential backoff. Non-retryable errors require manual intervention.
Best Practices
- Use incremental syncs — When possible, sync only new/changed data to reduce execution time
- Set appropriate timeouts — Configure timeouts based on expected data volume
- Monitor runs — Set up alerts for failed actions, especially in automations
- Test with small datasets — Validate action configuration before running on full data
- Use streaming for long runs — Subscribe to SSE for real-time progress on lengthy operations
OpenAPI Reference
For detailed endpoint schemas, request/response formats, and authentication: