API Triggers
Trigger workflows programmatically via the REST API
API Triggers
API triggers allow you to start workflows programmatically via the REST API. This is useful for on-demand processing, testing, or integrating with systems that don't support webhooks.
Configuration
API triggers have the simplest configuration:
{
"trigger": {
"type": "api"
}
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "api" |
That's it! The workflow can now be triggered via the REST API.
Running a Workflow
Basic Request
curl -X POST http://localhost:3000/api/v1/workflows/{WORKFLOW_ID}/run \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"customerId": "cus_xxx",
"amount": 9900
}
}'const response = await fetch(
`http://localhost:3000/api/v1/workflows/${workflowId}/run`,
{
method: 'POST',
headers: {
'x-api-key': process.env.API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
input: {
customerId: 'cus_xxx',
amount: 9900
}
})
}
);
const { data } = await response.json();
console.log('Run ID:', data.runId);import requests
response = requests.post(
f'http://localhost:3000/api/v1/workflows/{workflow_id}/run',
headers={
'x-api-key': api_key,
'Content-Type': 'application/json'
},
json={
'input': {
'customerId': 'cus_xxx',
'amount': 9900
}
}
)
data = response.json()['data']
print(f"Run ID: {data['runId']}")Request Body
{
"input": {
// Your workflow input data
},
"connectionId": "conn_xxx",
"idempotencyKey": "unique-key-123"
}| Field | Type | Required | Description |
|---|---|---|---|
input | object | No | Input data passed to the workflow |
connectionId | string | No | Specific connection to use |
idempotencyKey | string | No | Prevent duplicate runs |
Response
{
"success": true,
"data": {
"runId": "run_xyz789...",
"status": "pending",
"workflowId": "wf_abc123...",
"workflowVersion": 3
}
}| Field | Description |
|---|---|
runId | Unique identifier for this run |
status | Initial status (pending) |
workflowId | The workflow being executed |
workflowVersion | Version of the workflow used |
Input Data
The input object is available throughout your workflow as {{input}}:
Request:
{
"input": {
"customer": {
"id": "cus_xxx",
"email": "customer@example.com"
},
"items": [
{ "product": "Widget", "quantity": 2, "price": 1999 }
]
}
}In workflow steps:
{
"config": {
"to": "{{input.customer.email}}",
"subject": "Order for {{input.items.0.product}}"
}
}Input Validation
Define expected input with a JSON schema at the workflow level:
{
"name": "Create Invoice",
"trigger": {
"type": "api"
},
"inputSchema": {
"type": "object",
"required": ["customerId", "amount"],
"properties": {
"customerId": {
"type": "string",
"pattern": "^cus_[a-zA-Z0-9]+$"
},
"amount": {
"type": "number",
"minimum": 100
},
"currency": {
"type": "string",
"enum": ["usd", "eur", "gbp"],
"default": "usd"
}
}
},
"steps": [...]
}If validation fails, the API returns 400 Bad Request:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Input validation failed",
"details": [
{
"path": ["customerId"],
"message": "Required"
}
]
}
}Idempotency
Prevent duplicate workflow runs with idempotency keys:
curl -X POST http://localhost:3000/api/v1/workflows/{id}/run \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": { "orderId": "order_123" },
"idempotencyKey": "process-order-123"
}'If the same idempotencyKey is used again within 24 hours:
- The API returns the existing run instead of creating a new one
- No duplicate processing occurs
Best practices:
- Use meaningful keys:
process-order-{orderId},invoice-{invoiceId} - Include relevant identifiers from your input
- Keys expire after 24 hours
Checking Run Status
After triggering a workflow, poll for completion:
curl http://localhost:3000/api/v1/runs/{RUN_ID} \
-H "x-api-key: YOUR_API_KEY"async function waitForCompletion(runId, maxWait = 60000) {
const start = Date.now();
while (Date.now() - start < maxWait) {
const response = await fetch(
`http://localhost:3000/api/v1/runs/${runId}`,
{ headers: { 'x-api-key': API_KEY } }
);
const { data } = await response.json();
if (data.status === 'completed') {
return data.output;
}
if (data.status === 'failed') {
throw new Error(data.error?.message || 'Workflow failed');
}
// Wait 1 second before polling again
await new Promise(r => setTimeout(r, 1000));
}
throw new Error('Timeout waiting for workflow');
}Run Status Response
{
"success": true,
"data": {
"id": "run_xyz789...",
"status": "completed",
"input": { "customerId": "cus_xxx" },
"output": { "invoiceId": "inv_123" },
"startedAt": "2024-01-15T10:00:00.000Z",
"completedAt": "2024-01-15T10:00:05.000Z",
"steps": [
{
"id": "create-invoice",
"status": "completed",
"output": { "id": "inv_123" }
}
]
}
}Specifying a Connection
If your workflow uses multiple connections for the same provider, specify which one to use:
{
"input": { ... },
"connectionId": "conn_production_stripe"
}This is useful when:
- You have separate development and production connections
- Different customers use different OAuth connections
- You need to switch between sandbox and live environments
Error Handling
Common Error Responses
Workflow not found (404):
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Workflow not found"
}
}Workflow not active (400):
{
"success": false,
"error": {
"code": "INVALID_STATUS",
"message": "Workflow must be active to run. Current status: draft"
}
}Rate limited (429):
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Limit: 500 requests per minute."
}
}Invalid input (400):
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Input validation failed",
"details": [...]
}
}Rate Limits
API trigger endpoints have these limits:
| Category | Limit |
|---|---|
| Execute | 500 requests/minute per API key |
See Authentication for all rate limit details.
Use Cases
On-Demand Processing
Trigger workflows from your application:
// When user clicks "Generate Report"
await triggerWorkflow('wf_report_generator', {
input: {
reportType: 'monthly',
startDate: '2024-01-01',
endDate: '2024-01-31',
format: 'pdf'
}
});Testing Workflows
Test webhook workflows without setting up actual webhooks:
# Simulate a Stripe webhook
curl -X POST http://localhost:3000/api/v1/workflows/{id}/run \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"input": {
"type": "invoice.paid",
"data": {
"object": {
"id": "in_test",
"amount_paid": 9900
}
}
}
}'Batch Processing
Trigger multiple workflow runs for batch operations:
const customers = await getCustomersToProcess();
const runs = await Promise.all(
customers.map(customer =>
triggerWorkflow('wf_customer_sync', {
input: { customerId: customer.id },
idempotencyKey: `sync-${customer.id}-${today}`
})
)
);
console.log(`Started ${runs.length} sync workflows`);Scheduled via External Scheduler
If you use an external scheduler (cron job, cloud scheduler):
# In your crontab or cloud scheduler
0 9 * * * curl -X POST https://api.example.com/api/v1/workflows/wf_daily_report/run \
-H "x-api-key: $API_KEY" \
-d '{"input":{}}'Complete Example
{
"name": "Process Customer Order",
"description": "Create invoice and send confirmation for customer order",
"trigger": {
"type": "api"
},
"inputSchema": {
"type": "object",
"required": ["customerId", "items"],
"properties": {
"customerId": { "type": "string" },
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"productId": { "type": "string" },
"quantity": { "type": "number", "minimum": 1 },
"price": { "type": "number", "minimum": 0 }
}
}
},
"notes": { "type": "string" }
}
},
"steps": [
{
"id": "calculate-total",
"type": "transform",
"name": "Calculate Order Total",
"config": {
"mapping": {
"subtotal": "{{input.items | map: 'quantity * price' | sum}}",
"tax": "{{input.items | map: 'quantity * price' | sum | times: 0.08}}",
"total": "{{input.items | map: 'quantity * price' | sum | times: 1.08}}"
}
}
},
{
"id": "create-invoice",
"type": "action",
"name": "Create Invoice",
"action": "stripe.createInvoice",
"config": {
"customerId": "{{input.customerId}}",
"amount": "{{steps.calculate-total.output.total}}",
"metadata": {
"source": "api-order"
}
}
},
{
"id": "send-confirmation",
"type": "email",
"name": "Send Confirmation",
"config": {
"action": "send",
"to": "{{input.customerEmail}}",
"subject": "Order Confirmed - Invoice #{{steps.create-invoice.output.number}}",
"body": {
"html": "<p>Thank you for your order!</p><p>Total: ${{steps.calculate-total.output.total}}</p>"
}
}
}
]
}Troubleshooting
Workflow Not Starting
- Check workflow status - Must be
active - Verify API key - Needs
workflows:executescope - Check rate limits - May be rate limited
Input Not Available
- Verify input structure - Check JSON is valid
- Check template syntax - Use
{{input.field}} - Review inputSchema - May be stripping unknown fields
Run Stuck in Pending
- Check Trigger.dev - Ensure dev mode is running
- Verify connections - Required connections must be active
- Review logs - Check for execution errors