delayed-actions
The delayed actions feature allows users to schedule email actions (like labeling, archiving, or replying) to be executed after a specified delay period. This is useful for scenarios like: - **Follow-up reminders**: Label emails that haven't been replied to after X days
Install / Use
npx skills add TCYTseven/sourcemail-aiInstalls into whichever agent you are using.
Cursor Rules
Cursor IDE rules (v2)
Quality Score
Category
CommunicationSupported Platforms
Tags
Skill content
View source on GitHubDelayed Actions Feature
Overview
The delayed actions feature allows users to schedule email actions (like labeling, archiving, or replying) to be executed after a specified delay period. This is useful for scenarios like:
- Follow-up reminders: Label emails that haven't been replied to after X days
- Snooze functionality: Archive emails and bring them back later
- Time-sensitive processing: Apply actions only after a waiting period
Implementation Architecture
Core Components
-
Action Delay Configuration
Action.delayInMinutesfield: Optional delay from 1 minute to 90 days- UI controls in
RuleForm.tsxfor setting delays - Validation ensures delays are within acceptable bounds
-
Scheduled Action Storage
ScheduledActionmodel: Stores pending delayed actions- Contains action details, timing, and execution status
- Links to
ExecutedRulefor context and audit trail
-
QStash Integration
- Uses Upstash QStash for reliable message queuing
- Replaces cron-based polling with event-driven execution
- Provides built-in retries and error handling
Database Schema
model ScheduledAction {
id String @id @default(cuid())
executedRuleId String
actionType ActionType
messageId String
threadId String
scheduledFor DateTime
emailAccountId String
status ScheduledActionStatus @default(PENDING)
// Action-specific fields
label String?
subject String?
content String?
to String?
cc String?
bcc String?
url String?
// QStash integration
scheduledId String?
// Execution tracking
executedAt DateTime?
executedActionId String? @unique
// Relationships and indexes...
}
QStash Integration
Scheduling Process
-
Rule Execution: When a rule matches an email, actions are split into:
- Immediate actions: Executed right away
- Delayed actions: Scheduled via QStash
-
QStash Scheduling:
const notBefore = getUnixTime(addMinutes(new Date(), delayInMinutes)); const response = await qstash.publishJSON({ url: `${env.NEXT_PUBLIC_BASE_URL}/api/scheduled-actions/execute`, body: { scheduledActionId: scheduledAction.id, }, notBefore, // Unix timestamp for when to execute deduplicationId: `scheduled-action-${scheduledAction.id}`, }); -
Deduplication: Uses unique IDs to prevent duplicate execution
-
Message ID Storage: QStash scheduledId stored for efficient cancellation (field: scheduledId)
Execution Process
- QStash Delivery: QStash delivers message to
/api/scheduled-actions/execute - Signature Verification: Validates QStash signature for security
- Action Execution:
- Retrieves scheduled action from database
- Validates email still exists
- Executes the specific action using
runActionFunction - Updates execution status
Benefits Over Cron-Based Approach
- Reliability: No polling, exact scheduling, built-in retries
- Scalability: No background processes, QStash handles infrastructure
- Deduplication: Prevents duplicate execution with unique IDs
- Monitoring: Better observability through QStash dashboard
- Cancellation: Direct message cancellation using stored message IDs
Key Functions
Core Scheduling Functions
// Create and schedule a single delayed action
export async function createScheduledAction({
executedRuleId,
actionItem,
messageId,
threadId,
emailAccountId,
scheduledFor,
})
// Schedule multiple delayed actions for a rule execution
export async function scheduleDelayedActions({
executedRuleId,
actionItems,
messageId,
threadId,
emailAccountId,
})
// Cancel existing scheduled actions (e.g., when new rule overrides)
export async function cancelScheduledActions({
emailAccountId,
messageId,
threadId,
reason,
})
Usage in Rule Execution
// In run-rules.ts
// Cancel any existing scheduled actions for this message
await cancelScheduledActions({
emailAccountId: emailAccount.id,
messageId: message.id,
threadId: message.threadId,
reason: "Superseded by new rule execution",
});
// Schedule delayed actions if any exist
if (executedRule && delayedActions?.length > 0 && !isTest) {
await scheduleDelayedActions({
executedRuleId: executedRule.id,
actionItems: delayedActions,
messageId: message.id,
threadId: message.threadId,
emailAccountId: emailAccount.id,
});
}
Migration Safety
The database migration includes IF NOT EXISTS clauses to prevent conflicts:
-- CreateEnum
CREATE TYPE IF NOT EXISTS "ScheduledActionStatus" AS ENUM ('PENDING', 'EXECUTING', 'COMPLETED', 'FAILED', 'CANCELLED');
-- AlterTable
ALTER TABLE "Action" ADD COLUMN IF NOT EXISTS "delayInMinutes" INTEGER;
-- CreateTable
CREATE TABLE IF NOT EXISTS "ScheduledAction" (
-- table definition
);
-- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "ScheduledAction_executedActionId_key" ON "ScheduledAction"("executedActionId");
Usage Examples
Basic Delay Configuration
// In rule action configuration
{
type: "LABEL",
label: "Follow-up Needed",
delayInMinutes: 2880 // 2 days
}
Follow-up Workflow
- Email arrives and matches rule
- Immediate action: Archive email
- Delayed action: Label as "Follow-up" after 3 days
- If user replies before 3 days, action can be cancelled
API Endpoints
POST /api/scheduled-actions/execute: QStash webhook for executionDELETE /api/admin/scheduled-actions/[id]/cancel: Cancel scheduled actionPOST /api/admin/scheduled-actions/[id]/retry: Retry failed action
Error Handling
- Email Not Found: Action marked as completed with reason
- Execution Failure: Action marked as failed, logged for debugging
- Cancellation: QStash message cancelled, database updated
- Retries: QStash automatically retries failed deliveries
Monitoring
- Database status tracking: PENDING → EXECUTING → COMPLETED/FAILED
- QStash dashboard for message delivery monitoring
- Structured logging for debugging and observability
Related Skills
AstrBot
39.6kAI Agent Assistant & development framework that integrates lots of IM platforms, LLMs, plugins and AI feature, and can be your openclaw alternative. ✨
cmux
26.4kOpen source Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents. Built for multitasking, organization, and programmability.
lemonade
5.5kLemonade helps users discover and run local AI apps by serving optimized LLMs right from their own GPUs and NPUs. Join our discord: https://discord.gg/5xXzkMu8Zk
react-formengine-ai-form-builder-cursorrules-prompt-file
40.7kCursor rules for generating React forms from screenshots, PDFs, HTML, or text descriptions with validated FormEngine JSON schema. Renders through RSuite, Material UI, or Mantine.
Security Score
Audited on Invalid Date
