Crash-safe replay
A pending decision that never made it back is re-emitted on the next run. Killing a process mid-case is a non-event, not an incident.
Bookings that wait on a vendor. Approvals that wait on a human. Onboarding that spans a month. Caseflow makes long-lived business processes as reliable as a database transaction — written as plain code.
Decide looks at the case state and new events and returns what happens next. Execute runs one side effect. Everything else — persistence, retries, replay, timers, pauses — is the runtime's job.
Because Decide is deterministic, Caseflow can re-run it after any crash and land in exactly the same place. There is no workflow DSL to learn and no framework to structure your app around: it is Go code you can unit-test with a plain in-memory store.
type Handler interface {
// Decide is pure: case state + new events in,
// one decision out. The runtime replays it
// safely after any crash or redeploy.
Decide(ctx context.Context, view *CaseView) (*Decision, error)
// Execute runs one side effect. Errors become
// events your logic handles — never lost work.
Execute(ctx context.Context, c *Case, cmd *Command) (json.RawMessage, error)
}func (b *Booking) Decide(ctx context.Context,
view *caseflow.CaseView) (*caseflow.Decision, error) {
state := b.replay(view) // fold new events into state
switch {
case state.NeedsVendorRequest():
return execute("SEND_VENDOR_REQUEST", state)
case state.AwaitingCustomerReply():
return waitForEvent("CUSTOMER_REPLIED",
72*time.Hour, state)
case state.Confirmed():
return complete(state)
}
return waitUntil(state.NextFollowUp(), state)
}// Start a case — idempotent by ID.
err := engine.StartCase(ctx, "booking-8451",
"assisted_booking", input)
// Deliver what it is waiting for — from a webhook,
// an operator action, or a chat bot. The workflow
// resumes the moment the event lands.
err = engine.ResolveCurrentWait(ctx, "booking-8451",
&caseflow.Event{
Type: "CUSTOMER_REPLIED",
Source: "email",
Payload: payload,
})A case can live for minutes or for months. The loop is the same either way, and every step of it is persisted before it runs.
A command result, a customer reply, an operator decision, or a timeout — everything reaches the case as an ordered, immutable event.
The runtime hands it the case state and every event since the last run. It is pure and deterministic, so replaying it is always safe.
Execute a command, wait for an event with a deadline, sleep until a timestamp, or complete the case. Each outcome is persisted before it runs.
Lost responses are re-emitted, interrupted commands surface as failure events, and the loop picks up where the log ends.
A customer sends a request. An AI agent drafts the vendor email, a human supervisor approves it from a messenger, the vendor answers by email days later — and if they do not, the case nudges them. This is Caseflow's founding workload: it is the exact shape of the assisted-booking cases it runs in production today.
A customer request lands, with the original payload attached to the case.
An LLM agent reads the request and drafts an email to the vendor.
The draft goes to a supervisor on Telegram. The case pauses — for free.
One tap in the chat resolves the wait. The workflow resumes instantly.
The approved email goes out. Sending is a durable, retried command.
No vendor reply within 72 hours. The silence is just another event.
The reducer decides to send a polite reminder, then waits again.
The inbound reply arrives via webhook. The agent extracts the outcome.
The customer is notified and the case closes with its full history intact.
Long-lived workflows fail in ways request/response code never sees: processes die mid-side-effect, replies arrive after timeouts, retries collide with themselves. Caseflow's runtime is built around those cases, not around the happy path.
A pending decision that never made it back is re-emitted on the next run. Killing a process mid-case is a non-event, not an incident.
Commands are claimed with conditional writes. A duplicate execution attempt becomes an idempotent no-op instead of a double charge.
A command that dies mid-flight may or may not have had its effect. Caseflow reports it as a failure event and lets your logic decide — it never guesses.
Transient infrastructure failures retry automatically within a strict budget. Business failures become events immediately — no retry storms.
Every wait carries a deadline. Timeouts become events your logic handles, and a late reply still reaches the case afterwards.
Executions roll over automatically before platform history limits, and spin bugs — logic that loops without consuming events — are detected and failed fast.
Caseflow runs on managed AWS primitives with nothing for you to operate. A sleeping case consumes no compute — a workflow that waits three weeks for a reply costs what three weeks of nothing costs.
A domain-blind state machine drives the loop: run the decision, execute commands, pause on task tokens, sleep on timers. Waits cost nothing while they sleep.
Case state, commands, waits, and the strictly ordered event log live in DynamoDB with conditional writes guarding every contended transition.
Human input resumes the workflow the moment it arrives — a resolved wait releases its token immediately instead of polling on a schedule.
The state machine hiding in your glue code becomes one deterministic function with a complete audit log. No more reconciling a status column against reality.
Durable-execution platforms are powerful — and a real operational commitment. Caseflow gives you the same guarantees on serverless primitives, with no workers, shards, or upgrades to manage.
Compensation logic, retry budgets, and timeout handling are the runtime's job. Your code only answers one question: given what just happened, what happens next?
Caseflow runs production booking workflows today. Join the waitlist and we will reach out as hosted early access opens — bring one long-lived process and we will help you model it.
No spam — one email when early access opens, nothing else.