Initial push of Plunk Next
This commit is contained in:
@@ -0,0 +1,727 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-3.0.x"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directUrl = env("DIRECT_DATABASE_URL")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CORE MODELS
|
||||
// ============================================
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Credentials
|
||||
email String @unique
|
||||
password String?
|
||||
type AuthMethod
|
||||
|
||||
// Relations
|
||||
memberships Membership[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
name String
|
||||
|
||||
// API
|
||||
public String @unique
|
||||
secret String @unique
|
||||
|
||||
// Admin
|
||||
disabled Boolean @default(false)
|
||||
|
||||
// Billing
|
||||
customer String? @unique
|
||||
subscription String? @unique
|
||||
|
||||
// Billing Limits (per calendar month, null = unlimited)
|
||||
billingLimitWorkflows Int? // Max workflow emails per month
|
||||
billingLimitCampaigns Int? // Max campaign emails per month
|
||||
billingLimitTransactional Int? // Max transactional emails per month
|
||||
|
||||
// Email Tracking
|
||||
trackingEnabled Boolean @default(true) // Enable/disable open and click tracking
|
||||
|
||||
// Relations
|
||||
members Membership[]
|
||||
contacts Contact[]
|
||||
templates Template[]
|
||||
segments Segment[]
|
||||
workflows Workflow[]
|
||||
campaigns Campaign[]
|
||||
emails Email[]
|
||||
events Event[]
|
||||
domains Domain[]
|
||||
apiRequests ApiRequest[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("projects")
|
||||
}
|
||||
|
||||
model Membership {
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String
|
||||
project Project @relation(fields: [projectId], references: [id])
|
||||
projectId String
|
||||
|
||||
// Details
|
||||
role Role @default(MEMBER)
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@id([userId, projectId])
|
||||
@@map("memberships")
|
||||
}
|
||||
|
||||
model Domain {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Domain details
|
||||
domain String
|
||||
verified Boolean @default(false)
|
||||
|
||||
// DKIM tokens for DNS verification
|
||||
dkimTokens Json? // Array of DKIM token strings
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([projectId, domain])
|
||||
@@index([projectId])
|
||||
@@index([projectId, verified])
|
||||
@@map("domains")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CONTACTS & TEMPLATES
|
||||
// ============================================
|
||||
|
||||
model Contact {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
email String
|
||||
data Json? // Custom fields: { firstName: "John", plan: "pro", ... }
|
||||
|
||||
// Subscription
|
||||
subscribed Boolean @default(true)
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
emails Email[]
|
||||
workflowExecutions WorkflowExecution[]
|
||||
events Event[]
|
||||
segmentMemberships SegmentMembership[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([projectId, email])
|
||||
@@index([projectId])
|
||||
@@index([projectId, subscribed])
|
||||
@@map("contacts")
|
||||
}
|
||||
|
||||
model Template {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
name String
|
||||
description String?
|
||||
|
||||
// Content
|
||||
subject String
|
||||
body String // HTML content with {{variable}} placeholders
|
||||
from String
|
||||
fromName String?
|
||||
replyTo String?
|
||||
|
||||
// Type
|
||||
type TemplateType @default(MARKETING)
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
emails Email[]
|
||||
workflowSteps WorkflowStep[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([projectId])
|
||||
@@index([projectId, type])
|
||||
@@map("templates")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SEGMENTS (Dynamic audience groups)
|
||||
// ============================================
|
||||
|
||||
model Segment {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
name String
|
||||
description String?
|
||||
|
||||
// Filter conditions (evaluated dynamically)
|
||||
filters Json
|
||||
// Array of conditions with AND/OR logic:
|
||||
// [
|
||||
// { field: "data.plan", operator: "equals", value: "FREE" },
|
||||
// { field: "subscribed", operator: "equals", value: true },
|
||||
// { field: "emails.openedAt", operator: "within", value: 30, unit: "days" }
|
||||
// ]
|
||||
// Operators: equals, notEquals, contains, greaterThan, lessThan, within, exists, etc.
|
||||
|
||||
// Track membership changes (enables segment entry/exit events)
|
||||
trackMembership Boolean @default(false)
|
||||
|
||||
// Stats (computed)
|
||||
memberCount Int @default(0)
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
memberships SegmentMembership[]
|
||||
campaigns Campaign[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([projectId])
|
||||
@@map("segments")
|
||||
}
|
||||
|
||||
model SegmentMembership {
|
||||
// Relations
|
||||
contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade)
|
||||
contactId String
|
||||
|
||||
segment Segment @relation(fields: [segmentId], references: [id], onDelete: Cascade)
|
||||
segmentId String
|
||||
|
||||
// Membership tracking
|
||||
enteredAt DateTime @default(now())
|
||||
exitedAt DateTime? // null = currently in segment
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@id([contactId, segmentId])
|
||||
@@index([segmentId, exitedAt]) // Active members (exitedAt = null)
|
||||
@@index([contactId, exitedAt])
|
||||
@@index([enteredAt])
|
||||
@@map("segment_memberships")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CAMPAIGNS (One-time broadcasts)
|
||||
// ============================================
|
||||
|
||||
model Campaign {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
name String
|
||||
description String?
|
||||
status CampaignStatus @default(DRAFT)
|
||||
|
||||
// Email content
|
||||
subject String
|
||||
body String @db.Text
|
||||
from String
|
||||
fromName String?
|
||||
replyTo String?
|
||||
|
||||
// Audience selection
|
||||
audienceType CampaignAudienceType @default(ALL)
|
||||
audienceFilter Json? // For FILTERED: manual filter conditions
|
||||
|
||||
segment Segment? @relation(fields: [segmentId], references: [id])
|
||||
segmentId String? // For SEGMENT: reference to saved segment
|
||||
|
||||
// Scheduling
|
||||
scheduledFor DateTime?
|
||||
|
||||
// Stats (computed)
|
||||
totalRecipients Int @default(0)
|
||||
sentCount Int @default(0)
|
||||
deliveredCount Int @default(0)
|
||||
openedCount Int @default(0)
|
||||
clickedCount Int @default(0)
|
||||
bouncedCount Int @default(0)
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
emails Email[]
|
||||
|
||||
// Timestamps
|
||||
sentAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([projectId, status])
|
||||
@@index([scheduledFor])
|
||||
@@index([segmentId])
|
||||
@@map("campaigns")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// WORKFLOWS (Automated sequences)
|
||||
// ============================================
|
||||
|
||||
model Workflow {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Details
|
||||
name String
|
||||
description String?
|
||||
enabled Boolean @default(false)
|
||||
|
||||
// Entry point
|
||||
triggerType WorkflowTriggerType
|
||||
triggerConfig Json? // { eventName: "user.signup" } or { schedule: "0 9 * * *" }
|
||||
|
||||
// Re-entry behavior
|
||||
allowReentry Boolean @default(false) // If true, contacts can enter workflow multiple times
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
steps WorkflowStep[]
|
||||
executions WorkflowExecution[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([projectId])
|
||||
@@index([projectId, enabled])
|
||||
@@map("workflows")
|
||||
}
|
||||
|
||||
model WorkflowStep {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Step details
|
||||
type WorkflowStepType
|
||||
name String
|
||||
position Json // { x: 100, y: 200 } for visual editor
|
||||
|
||||
// Step configuration (flexible JSON)
|
||||
config Json
|
||||
// Examples by type:
|
||||
// TRIGGER: { eventName: "user.signup" }
|
||||
// SEND_EMAIL: { templateId: "uuid", from: "[email protected]" }
|
||||
// DELAY: { amount: 24, unit: "hours" }
|
||||
// WAIT_FOR_EVENT: { eventName: "email.clicked", timeout: 86400 }
|
||||
// CONDITION: { field: "contact.data.plan", operator: "equals", value: "pro" }
|
||||
// EXIT: { reason: "unsubscribed" }
|
||||
|
||||
// Relations
|
||||
workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade)
|
||||
workflowId String
|
||||
|
||||
template Template? @relation(fields: [templateId], references: [id])
|
||||
templateId String? // For SEND_EMAIL steps
|
||||
|
||||
// Graph connections
|
||||
outgoingTransitions WorkflowTransition[] @relation("FromStep")
|
||||
incomingTransitions WorkflowTransition[] @relation("ToStep")
|
||||
|
||||
// Execution tracking
|
||||
executions WorkflowStepExecution[]
|
||||
currentWorkflowExecutions WorkflowExecution[] // Executions currently on this step
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([workflowId])
|
||||
@@index([templateId])
|
||||
@@map("workflow_steps")
|
||||
}
|
||||
|
||||
model WorkflowTransition {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// From -> To
|
||||
fromStep WorkflowStep @relation("FromStep", fields: [fromStepId], references: [id], onDelete: Cascade)
|
||||
fromStepId String
|
||||
|
||||
toStep WorkflowStep @relation("ToStep", fields: [toStepId], references: [id], onDelete: Cascade)
|
||||
toStepId String
|
||||
|
||||
// Conditional routing
|
||||
condition Json? // null = always follow, or { branch: "yes" } for condition steps
|
||||
priority Int @default(0) // Order to evaluate transitions
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([fromStepId])
|
||||
@@index([toStepId])
|
||||
@@map("workflow_transitions")
|
||||
}
|
||||
|
||||
model WorkflowExecution {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relations
|
||||
workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade)
|
||||
workflowId String
|
||||
|
||||
contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade)
|
||||
contactId String
|
||||
|
||||
// Execution state
|
||||
status WorkflowExecutionStatus @default(RUNNING)
|
||||
|
||||
// Current position
|
||||
currentStep WorkflowStep? @relation(fields: [currentStepId], references: [id])
|
||||
currentStepId String?
|
||||
|
||||
// Exit information
|
||||
exitReason String?
|
||||
|
||||
// Context data (merged with contact.data when executing)
|
||||
context Json? // Additional variables for this execution
|
||||
|
||||
// Relations
|
||||
stepExecutions WorkflowStepExecution[]
|
||||
emails Email[]
|
||||
|
||||
// Timestamps
|
||||
startedAt DateTime @default(now())
|
||||
completedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([workflowId, contactId]) // Index for querying executions by workflow and contact
|
||||
@@index([workflowId, status])
|
||||
@@index([contactId, status])
|
||||
@@index([status, currentStepId])
|
||||
@@map("workflow_executions")
|
||||
}
|
||||
|
||||
model WorkflowStepExecution {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Relations
|
||||
execution WorkflowExecution @relation(fields: [executionId], references: [id], onDelete: Cascade)
|
||||
executionId String
|
||||
|
||||
step WorkflowStep @relation(fields: [stepId], references: [id], onDelete: Cascade)
|
||||
stepId String
|
||||
|
||||
// Execution state
|
||||
status StepExecutionStatus @default(PENDING)
|
||||
|
||||
// Delay/scheduling
|
||||
scheduledFor DateTime? // When this step should execute (for DELAY steps)
|
||||
executeAfter DateTime? // Don't execute before this time (for WAIT_FOR_EVENT timeout)
|
||||
|
||||
// Result tracking
|
||||
output Json? // Step execution result
|
||||
error String?
|
||||
|
||||
// Relations
|
||||
emails Email[] // Emails sent by this step execution
|
||||
|
||||
// Timestamps
|
||||
startedAt DateTime?
|
||||
completedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([executionId, status])
|
||||
@@index([stepId])
|
||||
@@index([status, scheduledFor]) // For delay queue processor
|
||||
@@index([scheduledFor])
|
||||
@@map("workflow_step_executions")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EMAILS (Unified tracking for all sends)
|
||||
// ============================================
|
||||
|
||||
model Email {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Recipient
|
||||
contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade)
|
||||
contactId String
|
||||
|
||||
// Content (denormalized for history)
|
||||
subject String
|
||||
body String // Rendered HTML
|
||||
from String
|
||||
fromName String?
|
||||
replyTo String?
|
||||
headers Json? // Custom email headers
|
||||
attachments Json? // Array of {filename: string, content: string (base64), contentType: string}
|
||||
|
||||
// AWS SES Message ID (for tracking webhooks)
|
||||
messageId String? @unique
|
||||
|
||||
// Source - identifies how this email was triggered
|
||||
sourceType EmailSourceType
|
||||
|
||||
// Relations to source (one will be set based on sourceType)
|
||||
template Template? @relation(fields: [templateId], references: [id])
|
||||
templateId String? // For TRANSACTIONAL
|
||||
|
||||
campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: Cascade)
|
||||
campaignId String? // For CAMPAIGN
|
||||
|
||||
workflowExecution WorkflowExecution? @relation(fields: [workflowExecutionId], references: [id], onDelete: Cascade)
|
||||
workflowExecutionId String? // For WORKFLOW
|
||||
|
||||
workflowStepExecution WorkflowStepExecution? @relation(fields: [workflowStepExecutionId], references: [id], onDelete: Cascade)
|
||||
workflowStepExecutionId String? // For WORKFLOW (specific step)
|
||||
|
||||
// Delivery
|
||||
status EmailStatus @default(PENDING)
|
||||
|
||||
// Event timestamps
|
||||
sentAt DateTime?
|
||||
deliveredAt DateTime?
|
||||
openedAt DateTime? // First open
|
||||
clickedAt DateTime? // First click
|
||||
bouncedAt DateTime?
|
||||
complainedAt DateTime?
|
||||
|
||||
// Event counts
|
||||
opens Int @default(0)
|
||||
clicks Int @default(0)
|
||||
|
||||
// Error tracking
|
||||
error String?
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
events Event[]
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([projectId, contactId])
|
||||
@@index([contactId])
|
||||
@@index([campaignId])
|
||||
@@index([workflowExecutionId])
|
||||
@@index([workflowStepExecutionId])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@index([projectId, sourceType, createdAt]) // For billing limit queries
|
||||
@@map("emails")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// EVENTS (Webhook tracking & workflow triggers)
|
||||
// ============================================
|
||||
|
||||
model Event {
|
||||
id String @id @default(uuid())
|
||||
|
||||
// Event details
|
||||
name String // e.g., "email.opened", "email.clicked", "user.signup"
|
||||
data Json? // Event payload
|
||||
|
||||
// Relations
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
projectId String
|
||||
|
||||
contact Contact? @relation(fields: [contactId], references: [id], onDelete: Cascade)
|
||||
contactId String?
|
||||
|
||||
email Email? @relation(fields: [emailId], references: [id], onDelete: Cascade)
|
||||
emailId String?
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([projectId, name])
|
||||
@@index([contactId])
|
||||
@@index([emailId])
|
||||
@@index([createdAt])
|
||||
@@map("events")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ENUMS
|
||||
// ============================================
|
||||
|
||||
enum AuthMethod {
|
||||
PASSWORD
|
||||
GOOGLE_OAUTH
|
||||
GITHUB_OAUTH
|
||||
}
|
||||
|
||||
enum Role {
|
||||
OWNER
|
||||
ADMIN
|
||||
MEMBER
|
||||
}
|
||||
|
||||
enum TemplateType {
|
||||
TRANSACTIONAL
|
||||
MARKETING
|
||||
}
|
||||
|
||||
enum CampaignStatus {
|
||||
DRAFT
|
||||
SCHEDULED
|
||||
SENDING
|
||||
SENT
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
enum CampaignAudienceType {
|
||||
ALL // All contacts
|
||||
FILTERED // Based on audienceFilter
|
||||
SEGMENT // Predefined segment
|
||||
}
|
||||
|
||||
enum WorkflowTriggerType {
|
||||
EVENT // Triggered by an event (e.g., user.signup)
|
||||
MANUAL // Manually started via API
|
||||
SCHEDULE // Runs on a schedule (cron)
|
||||
}
|
||||
|
||||
enum WorkflowStepType {
|
||||
TRIGGER // Entry point
|
||||
SEND_EMAIL // Send an email
|
||||
DELAY // Wait for X time
|
||||
WAIT_FOR_EVENT // Wait for specific event (with timeout)
|
||||
CONDITION // If/else branching
|
||||
EXIT // Early exit point
|
||||
WEBHOOK // Call external webhook
|
||||
UPDATE_CONTACT // Update contact fields
|
||||
}
|
||||
|
||||
enum WorkflowExecutionStatus {
|
||||
RUNNING // Currently executing
|
||||
WAITING // Waiting for event or delay
|
||||
COMPLETED // Finished normally
|
||||
EXITED // Exited early (via EXIT step)
|
||||
FAILED // Failed with error
|
||||
CANCELLED // Manually cancelled
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// API REQUEST LOGGING
|
||||
// ============================================
|
||||
|
||||
model ApiRequest {
|
||||
id String @id // Request ID (UUID from X-Request-ID header)
|
||||
|
||||
// Request metadata
|
||||
method String // HTTP method (GET, POST, etc.)
|
||||
path String // Request path (/v1/send, /contacts, etc.)
|
||||
statusCode Int // HTTP response status code
|
||||
duration Int // Response time in milliseconds
|
||||
|
||||
// Authentication context
|
||||
projectId String? // Project that made the request (if authenticated)
|
||||
project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull)
|
||||
userId String? // User that made the request (if JWT auth)
|
||||
authType String? // "apiKey" or "jwt"
|
||||
|
||||
// Request details
|
||||
ip String? // Client IP address
|
||||
userAgent String? // User-Agent header
|
||||
|
||||
// Error information (if status >= 400)
|
||||
errorCode String? // Machine-readable error code (VALIDATION_ERROR, etc.)
|
||||
errorMessage String? // Human-readable error message
|
||||
|
||||
// Size tracking (for analytics)
|
||||
requestSize Int? // Request body size in bytes
|
||||
responseSize Int? // Response body size in bytes
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
// Indexes for efficient querying
|
||||
@@index([projectId, createdAt(sort: Desc)]) // Query requests by project, most recent first
|
||||
@@index([userId, createdAt(sort: Desc)]) // Query requests by user
|
||||
@@index([statusCode, createdAt(sort: Desc)]) // Query errors (WHERE statusCode >= 400)
|
||||
@@index([path, createdAt(sort: Desc)]) // Query by endpoint
|
||||
@@index([createdAt(sort: Desc)]) // General time-based queries
|
||||
@@index([projectId, statusCode, createdAt(sort: Desc)]) // Composite for project error queries
|
||||
@@map("api_requests")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ENUMS
|
||||
// ============================================
|
||||
|
||||
enum StepExecutionStatus {
|
||||
PENDING // Not yet started
|
||||
SCHEDULED // Scheduled for future execution (DELAY)
|
||||
WAITING // Waiting for event (WAIT_FOR_EVENT)
|
||||
RUNNING // Currently executing
|
||||
COMPLETED // Successfully completed
|
||||
SKIPPED // Skipped (e.g., condition not met)
|
||||
FAILED // Failed with error
|
||||
}
|
||||
|
||||
enum EmailSourceType {
|
||||
TRANSACTIONAL // Sent via API call
|
||||
CAMPAIGN // Sent as part of broadcast
|
||||
WORKFLOW // Sent via workflow automation
|
||||
}
|
||||
|
||||
enum EmailStatus {
|
||||
PENDING // Queued for sending
|
||||
SENDING // Currently being sent
|
||||
SENT // Successfully sent to provider
|
||||
DELIVERED // Confirmed delivered
|
||||
OPENED // Recipient opened email
|
||||
CLICKED // Recipient clicked link
|
||||
BOUNCED // Bounced (hard or soft)
|
||||
COMPLAINED // Marked as spam
|
||||
FAILED // Failed to send
|
||||
}
|
||||
Reference in New Issue
Block a user