Workflow Automation¶
!!! info "TL;DR" Workflows trigger automated actions when events occur. Powered by Inngest for durable execution. Built-in actions: send email, create GitHub issue, update status, Slack notify. Custom actions supported.
What is a Workflow?¶
A workflow is a set of automated actions that execute when a trigger condition is met. For example:
- Trigger: User submits feedback with "bug" in the title
- Action 1: Create a GitHub issue
- Action 2: Send email to engineering team
- Action 3: Update submission status to "Under Review"
Workflows are durable. If an action fails (e.g., GitHub is down), Inngest retries with exponential backoff. The workflow resumes from the failed step, not from the beginning.
Workflow Definition¶
A workflow consists of:
- Trigger — The event that starts the workflow
- Conditions — Optional filters on the trigger
- Actions — Steps to execute in sequence
Example Workflow¶
{
"name": "Triage Bug Reports",
"trigger": {
"type": "submission_created",
"conditions": {
"type": "problem",
"title_contains": "bug"
}
},
"actions": [
{
"type": "create_github_issue",
"params": {
"repo": "Revoir-Software/revoir-apps",
"labels": ["bug", "user-reported"],
"title": "{{submission.title}}",
"body": "{{submission.description}}\n\nReported by: {{submission.author.email}}"
}
},
{
"type": "send_email",
"params": {
"to": "engineering@revoir.app",
"subject": "New bug report: {{submission.title}}",
"template": "bug_report"
}
},
{
"type": "update_status",
"params": {
"status": "under_review"
}
}
]
}
Triggers¶
Workflows can trigger on these events:
submission_created¶
Fires when a user submits feedback.
Conditions:
type— Filter byideaorproblemcategory— Filter by category name or IDtitle_contains— Substring match on titledescription_contains— Substring match on description
vote_threshold¶
Fires when a submission reaches a vote count.
Conditions:
threshold— Minimum votes (e.g.,10,50,100)category— Optional category filter
survey_response¶
Fires when a user completes a survey.
Conditions:
survey_id— Specific surveynps_score— Filter by NPS value (e.g.,< 7for detractors)sentiment— Filter bypositive,neutral,negative
status_change¶
Fires when submission status changes.
Conditions:
from_status— Old statusto_status— New status
scheduled¶
Fires on a cron schedule.
Conditions:
cron— Cron expression (e.g.,0 9 * * MONfor 9am every Monday)
Built-in Actions¶
send_email¶
Sends email via Resend.
Params:
to— Recipient email (string or array)subject— Email subjecttemplate— Named template fromlib/email-templates/context— Variables for template rendering
Example:
{
"type": "send_email",
"params": {
"to": "{{submission.author.email}}",
"subject": "Your feedback is under review",
"template": "feedback_update",
"context": {
"name": "{{submission.author.name}}",
"title": "{{submission.title}}"
}
}
}
create_github_issue¶
Creates a GitHub issue.
Params:
repo— Full repo name (e.g.,Revoir-Software/revoir-apps)title— Issue titlebody— Issue body (supports markdown)labels— Array of label namesassignees— Array of GitHub usernames
Example:
{
"type": "create_github_issue",
"params": {
"repo": "Revoir-Software/revoir-apps",
"title": "{{submission.title}}",
"body": "{{submission.description}}",
"labels": ["user-feedback", "{{submission.category}}"]
}
}
update_status¶
Updates submission status.
Params:
status— New status (enum:open,under_review,planned,in_progress,shipped,declined)
Example:
slack_notify¶
Sends a Slack message.
Params:
channel— Channel name (e.g.,#product)message— Message text (supports Slack markdown)blocks— Optional Slack Block Kit blocks
Example:
{
"type": "slack_notify",
"params": {
"channel": "#product",
"message": "New feedback with 10+ votes: {{submission.title}} ({{submission.vote_count}} votes)"
}
}
Template Variables¶
Actions support template variables using {{variable}} syntax. Available variables depend on the trigger:
Submission triggers:
{{submission.id}}{{submission.title}}{{submission.description}}{{submission.vote_count}}{{submission.author.name}}{{submission.author.email}}{{submission.category}}{{submission.url}}
Survey triggers:
{{survey.id}}{{survey.title}}{{response.id}}{{response.nps_score}}{{response.sentiment}}{{response.answers}}
Custom Actions¶
You can define custom action handlers in lib/workflow/actions/. Each handler implements:
interface ActionHandler {
type: string
execute(params: unknown, context: WorkflowContext): Promise<ActionResult>
}
Example custom action:
// lib/workflow/actions/create-zendesk-ticket.ts
export class CreateZendeskTicketAction implements ActionHandler {
type = 'create_zendesk_ticket'
async execute(params: unknown, context: WorkflowContext) {
const { subject, description, priority } = params as {
subject: string
description: string
priority: 'low' | 'normal' | 'high' | 'urgent'
}
const ticket = await zendeskClient.tickets.create({
subject,
comment: { body: description },
priority,
})
return {
success: true,
data: { ticket_id: ticket.id },
}
}
}
Register it in lib/workflow/actions/index.ts:
export const actionHandlers = new Map<string, ActionHandler>([
['send_email', new SendEmailAction()],
['create_github_issue', new CreateGitHubIssueAction()],
['create_zendesk_ticket', new CreateZendeskTicketAction()],
// ...
])
Durable Execution¶
Workflows use Inngest for durable execution. If an action fails:
- Inngest logs the error
- Retries with exponential backoff (5 attempts by default)
- If all retries fail, marks the workflow as
failed - Sends alert to monitoring channel
The workflow state is persisted in Inngest's cloud. You can view execution history in the Inngest dashboard.
Workflow Management¶
Creating Workflows¶
Via the admin dashboard:
- Navigate to Settings → Workflows
- Click Create Workflow
- Select a trigger type
- Add conditions
- Add actions
- Save
Via the API:
curl -X POST https://canviq.app/api/admin/workflows \
-H "Authorization: Bearer your-api-key" \
-d '{"name": "...", "trigger": {...}, "actions": [...]}'
Listing Workflows¶
Triggering Manually¶
curl -X POST https://canviq.app/api/admin/workflows/:id/trigger \
-H "Authorization: Bearer your-api-key" \
-d '{"context": {"submission_id": "..."}}'
What's Next¶
- Available Tools —
create_workflowandtrigger_workflowtools - Natural Language Mode — Describe workflows in plain English
- GitHub Integration — GitHub, Slack, Zendesk setup