Trigger.dev started as a developer-friendly Zapier alternative and pivoted to become a Temporal alternative. That evolution exposes what engineers actually need when building durable workflows: not webhook glue, but code-native orchestration with retry logic, state management, and observability baked in.
The platform lets you write event-driven background tasks directly in TypeScript. Instead of clicking through UI boxes to connect APIs, you define triggers, actions, and error handling in code. The v2 architecture shift (October 2023) moved from webhook-centric automation to durable execution primitives, competing with Temporal rather than Zapier.
Architecture: Code-First vs. Connector-Based
Zapier model:
- No-code UI defines workflows as trigger → action chains
- Pre-built connectors abstract API calls
- Retry logic and error handling are black-box platform features
- State lives in Zapier’s infrastructure
- Observability limited to execution logs in web dashboard
Trigger.dev model:
- TypeScript functions define tasks with explicit retry, timeout, and concurrency controls
- Developers call APIs directly or use SDK integrations
- Retry logic, idempotency, and failure recovery are code-level primitives
- State management through durable execution guarantees
- Observability via real-time tracing and structured logs
The key difference: Zapier abstracts the plumbing. Trigger.dev exposes it.
Execution Model: Durable Tasks vs. Webhook Chains
Trigger.dev tasks run as long-lived, resumable functions. If a task fails mid-execution, the platform retries from the last checkpoint, not from the beginning. This matters for workflows that call rate-limited APIs, process large datasets, or coordinate multi-step operations.
Durable execution guarantees:
- Tasks survive infrastructure failures and restarts
- Automatic checkpointing between steps
- Configurable retry policies per task
- Queue-based concurrency controls
Example task structure:
import { task } from "@trigger.dev/sdk/v3";
export const processOrder = task({
id: "process-order",
retry: {
maxAttempts: 3,
factor: 2,
minTimeout: 1000,
},
run: async (payload: { orderId: string }) => {
// Step 1: Charge payment (checkpointed)
const charge = await stripe.charges.create({
amount: payload.amount,
source: payload.token,
});
// Step 2: Update inventory (checkpointed)
await db.inventory.decrement(payload.items);
// Step 3: Send confirmation (checkpointed)
await sendEmail({
to: payload.email,
template: "order-confirmation",
data: { orderId: payload.orderId },
});
return { status: "completed", chargeId: charge.id };
},
});
If the email service fails, the task retries only the sendEmail step. The payment and inventory updates already succeeded and won’t re-execute.
State Management and Idempotency
Zapier handles idempotency through deduplication windows (typically 1 hour). If the same trigger fires twice, Zapier skips the duplicate. This works for simple webhooks but breaks down for stateful workflows.
Trigger.dev uses task IDs and run IDs to guarantee exactly-once execution:
- Task ID: Unique identifier for the workflow definition
- Run ID: Unique identifier for each execution instance
- Idempotency keys: Optional developer-defined keys for custom deduplication
You control when tasks are idempotent and when they should run multiple times for the same input.
Deployment Models and Cost Structure
| Aspect | Zapier | Trigger.dev (Cloud) | Trigger.dev (Self-Hosted) |
|---|---|---|---|
| Hosting | Fully managed SaaS | Managed control plane + your compute | Your infrastructure |
| Execution isolation | Shared multi-tenant | Isolated containers per task | Your container runtime |
| Cold starts | N/A (always warm) | ~500ms for TypeScript tasks | Depends on your setup |
| Scaling | Automatic, opaque | Elastic scaling with queue depth | You manage workers |
| Cost model | Per-task pricing tiers | Compute time + task executions | Infrastructure costs only |
| Observability | Web dashboard only | Real-time tracing + custom exports | Full control over telemetry |
The self-hosted option matters for compliance, data residency, and cost control at scale. You run the Trigger.dev runtime in your VPC and connect it to the control plane for orchestration.
Retry Logic and Failure Modes
Zapier retries failed tasks automatically with exponential backoff, but you can’t customize the policy per workflow. Trigger.dev exposes retry configuration at the task level:
retry: {
maxAttempts: 5,
factor: 2, // Exponential backoff multiplier
minTimeout: 1000, // Start at 1 second
maxTimeout: 60000, // Cap at 60 seconds
randomize: true, // Add jitter to prevent thundering herd
}
Common failure modes:
- API rate limits: Use
wait.for()to pause execution until rate limit resets - Transient network errors: Automatic retry with backoff
- Permanent failures: Dead letter queue for manual inspection
- Timeout exhaustion: Configurable per-task timeout with graceful shutdown
The platform tracks failure reasons and surfaces them in the observability layer. You can query failed runs, inspect payloads, and replay tasks from the dashboard or API.
Observability: Logs vs. Traces
Zapier shows execution logs as a linear timeline. Trigger.dev provides distributed tracing with OpenTelemetry integration:
- Span-based tracing: Each task step creates a trace span
- Structured logging: JSON logs with correlation IDs
- Real-time monitoring: WebSocket-based live tail
- Custom metrics: Export to Prometheus, Datadog, or your telemetry stack
You can trace a task execution across multiple services, see where time is spent, and identify bottlenecks. This matters when debugging complex workflows or optimizing performance.
Tool Boundaries: Connectors vs. SDK Integrations
Zapier’s connector model abstracts API details. You select “Create Contact in HubSpot” from a dropdown, map fields, and the platform handles authentication and API calls.
Trigger.dev provides SDK integrations for common services (Stripe, Resend, OpenAI) but expects you to call APIs directly when needed:
import { openai } from "@trigger.dev/openai";
import { stripe } from "@trigger.dev/stripe";
export const generateInvoice = task({
id: "generate-invoice",
run: async ({ customerId, items }) => {
// Direct API call with SDK helper
const customer = await stripe.customers.retrieve(customerId);
// Generate PDF with AI
const pdf = await openai.files.create({
purpose: "invoice",
file: await generatePDF(items),
});
return { invoiceUrl: pdf.url };
},
});
This gives you full control over request parameters, error handling, and response parsing. The trade-off: you write more code but avoid connector limitations.
The v1 to v2 Pivot: What Changed
v1 (Zapier alternative):
- Webhook-centric triggers
- Pre-built integrations for common SaaS tools
- Focus on replacing no-code automation
v2 (Temporal alternative):
- Durable execution primitives
- Long-running task support (hours or days)
- Workflow orchestration for complex state machines
- Elastic scaling with queue-based concurrency
The pivot happened because developers wanted workflow orchestration, not just webhook glue. They needed to coordinate multi-step processes, handle retries intelligently, and maintain state across failures.
When to Use Trigger.dev vs. Zapier
Use Trigger.dev when:
- You need custom retry logic or failure handling
- Workflows involve complex state management
- You want observability integrated with your existing telemetry stack
- Tasks run longer than a few minutes
- You need to self-host for compliance or cost reasons
- Your team writes TypeScript and prefers code over UI
Use Zapier when:
- Non-technical users need to build automations
- Workflows are simple trigger → action chains
- You want pre-built connectors for 5,000+ apps
- Speed of setup matters more than customization
- You don’t need sub-minute execution guarantees
Technical Verdict
Trigger.dev solves the workflow orchestration problem for teams that already write code. It’s not a Zapier replacement for marketing teams building lead nurture sequences. It’s infrastructure for engineers building durable, observable task pipelines.
The platform shines when you need retry logic that respects API rate limits, state management across long-running processes, or observability that integrates with your existing stack. It struggles when non-technical users need to build workflows or when you need connectors for niche SaaS tools.
The self-hosted option makes it viable for regulated industries or high-volume workloads where per-task pricing becomes expensive. The TypeScript-native API means your workflows live in version control, get code-reviewed, and deploy through CI/CD.
If you’re building agentic systems that coordinate multiple LLM calls, tool invocations, and external API interactions, Trigger.dev provides the orchestration layer those agents need. It’s plumbing, not magic, and that’s the point.
Source Links
- Primary source: Trigger.dev
- Show HN discussion: Hacker News (745 points, 190 comments)
- GitHub repository: triggerdotdev/trigger.dev
- v2 announcement: Hacker News (172 points, 39 comments)