Temporal makes a function behave as if it never crashes; Encore makes infrastructure behave as if it was always there. They solve different halves of the same backend, which is why a lot of Encore's users run them together.
A Temporal Workflow reads like ordinary top-to-bottom code: a function charges a card, then ships the order a day later. It keeps reading that way even when the worker running it crashes and comes back halfway through, and you never write the code that makes that safe. In some ways, one of the most useful benefits of Temporal is that it provides a simple mental model for a really complex problem.
We built Encore on the same idea, applied to a different hard problem: provisioning and orchestrating distributed systems across environments. Encore gives you automated infrastructure from local dev to your production cloud on AWS or GCP, with full control through your own defaults and guardrails. You write the code, the infrastructure provisions itself within the defaults you set, and you build as if it was already there.
Together, Temporal and Encore let you write a backend as if the long-running process and the infrastructure under it had always existed.
When a crash isn't just a retry#
Most of a backend is request in, response out, where a crash just means a failed request the caller retries. This is the logic that can't be retried like that: the charge in that order has to happen once, and if the worker dies mid-way the rest has to resume, not start over.
Teams usually build this themselves first, with a queue, a status column, and a pile of retry code. It holds up until the incident reports start coming in, and the pattern in them is plumbing: a worker that dies halfway through a job and loses it, or a retry that fires twice and charges a customer again.
Temporal: the function that always ran#
Anyone running Temporal has already solved this half, so we'll keep it short: a long-running, multi-step process like that is exactly what Temporal is built for.
Temporal persists workflow state at every step, so when a worker crashes it replays from the last completed activity instead of starting over. A side effect that already succeeded is never run again, and when the process has to wait, for a payment to clear or a person to approve, the workflow waits for a signal, for thirty seconds or for three days, without holding a thread. Every run leaves a complete, replayable history:
// A durable order workflow. Each step is an activity: it retries on
// failure and is never re-run once it has succeeded, even if the
// worker crashes and Temporal replays from the last completed step.
export async function orderProcessingWorkflow(order: OrderInput): Promise<OrderResult> {
await createOrder(order);
if (!(await checkInventory(order))) {
return { orderId: order.orderId, status: "failed", error: "out of stock" };
}
const paymentId = await processPayment(order);
try {
const trackingId = await shipOrder(order);
await sendConfirmationEmail(order, trackingId); // best-effort
return { orderId: order.orderId, status: "completed", paymentId, trackingId };
} catch (err) {
await refundPayment(paymentId); // saga compensation
return { orderId: order.orderId, status: "failed", paymentId, error: "shipping failed, payment refunded" };
}
}
A worker that dies mid-job or a retry that fires twice no longer turns into an incident, because the workflow handles the recovery you'd otherwise write by hand.
Encore: the infrastructure that was always there#
The first thing most teams notice about Encore is the typed API. The endpoint that starts a workflow and the activities that call back into your services are ordinary typed functions, checked at compile time, so there's no separate schema to keep in sync and no client to regenerate by hand: the contract is the code. The infrastructure works the same way. You declare the database and the Temporal credential in code, and Encore provisions them in every environment, in your own cloud account. The code carries only what the application needs to run; how each environment is configured and secured lives separately, owned by whoever runs production. Developers and agents stop waiting on a ticket to get infrastructure, and the platform team stops reviewing Terraform PRs to hand it to them, free to set the policy for each environment instead.
import { api } from "encore.dev/api";
import { secret } from "encore.dev/config";
import { SQLDatabase } from "encore.dev/storage/sqldb";
const db = new SQLDatabase("orders", { migrations: "./migrations" });
const temporalKey = secret("TemporalAPIKey");
// A typed Encore API that starts the workflow. The request type IS the
// contract; a caller that sends the wrong shape fails to compile.
export const placeOrder = api(
{ method: "POST", path: "/orders", expose: true, auth: true },
async (order: OrderInput): Promise<{ workflowId: string }> => {
const client = await getTemporalClient(temporalKey());
const handle = await client.workflow.start("orderProcessingWorkflow", {
args: [order], taskQueue: "orders", workflowId: order.orderId,
});
return { workflowId: handle.workflowId };
},
);
// createOrder runs inside the workflow as an activity, so the DB write is
// durable: Temporal retries it on failure and never repeats it once it
// has succeeded.
export async function createOrder(order: OrderInput): Promise<void> {
await db.exec`INSERT INTO orders (id, status) VALUES (${order.orderId}, 'processing')`;
}
The Temporal worker runs as an ordinary Encore service. It automatically starts on service initialization, using an Encore secret for securely connecting to the Temporal cluster. The Temporal activities reach your other services through Encore's typed clients. Locally, you run the Temporal dev server in one terminal and encore run in another, and the whole backend runs on a laptop the way it runs in production. When something misbehaves, Temporal's Web UI shows the workflow's execution history, and Encore's dashboard traces the API request that started it down into the activity calls and the queries they made. A failure that crosses that async boundary usually means stitching logs from two systems together by hand; here you get both views with no instrumentation code, and follow one request from the API call to the activity that broke.
Wiring them together#
Because the two own different things, durability over time versus the backend around it, the seam between them is small. Standing a worker up as an Encore service is a one-time piece of glue, and from there the workflow is just another part of the system you declare in code and deploy with everything else.
"For us, Encore and Temporal fit together naturally. Encore gives us a clean operational backend model, and Temporal gives us a reliable way to run workflows that live over time, wait for events, react to signals, and keep a clear history. That combination is especially valuable when you are building software-first operations, not just a CRUD app."
— Josef Sima, Dreem (dreem.cz, a software-first real estate company)
For Dreem that means treating software as an operational system, where each business process gets its own identity, history, and next step.
When an agent writes the service#
Most backend code is written by AI agents, and an agent is itself a long-running process, a chain of model and tool calls with waits in between. That makes an agent run the same can't-fail-halfway logic from the start of this post: if it crashes partway through, you don't want to re-run the model calls or re-fire the tools, you want it to resume where it left off. The same agents are also writing the services around these runs, where a subtle retry bug or a missing rollback ships just as easily.
On Encore, a generated endpoint that doesn't match its types fails to compile before it ships, and its MCP server gives the agent enough context about your services that what it writes fits them in the first place. Because the defaults are yours, an agent adding a service builds inside the guardrails your team already set, inheriting your conventions for how a database or an endpoint gets provisioned rather than inventing its own. The logic that actually runs is Temporal's job: in a workflow it picks up retries and replay, and an approval gate is just a signal the run blocks on.
"Encore helps us ship microservices and infrastructure at pace, and Temporal ensures that our AI agent can be durable to any failures. We've combined them together seamlessly with a simple wrapper, so now we get the best of both worlds."
— Neal Lathia, CTO, Gradient Labs
Try it#
If you run Temporal today, you don't need to change anything about your workflows to see whether this fits. Stand up one worker as an Encore service, point it at your existing Temporal namespace, and run it locally. We wrote a step-by-step guide for wiring the two together: Temporal + Encore. Encore is free to start and deploys into your own AWS or GCP account.