Modern business processes are rarely a single step. Consider a new user signing up for your service. It's not just one event; it's a cascade of operations: create a database record, provision a workspace, send a welcome email, notify the sales team, and perhaps update analytics. If any single step fails, what happens to the rest? You risk leaving users in a broken, inconsistent state.
This is where the power of building with fundamental blocks comes into play. At action.do, we believe that all reliable automation is built upon atomic actions—single, indivisible tasks that either succeed completely or fail entirely. They are the bedrock of flawless execution.
But how do you connect these individual bricks to build a robust, multi-step service? In this post, we'll explore how to go from a single action.do call to a complex, resilient business process by chaining actions with an orchestrator like workflow.do.
Before we build a workflow, let's understand its core component. An atomic action is a guarantee. It's an operation that promises to leave no partial or corrupt state. When you execute an action.do task to update a customer record, it either finishes 100% or it rolls back, leaving the system as if the action never happened.
This atomicity is what separates a professional-grade automation platform from a simple function call. While a function can fail halfway through, action.do wraps your logic with built-in:
Let's look at a simple, concrete example: sending a welcome email.
import { DotDo } from '@do-platform/sdk';
const client = new DotDo({ apiKey: 'YOUR_API_KEY' });
// Define and execute a predefined atomic action, 'send-welcome-email'
async function sendWelcomeEmail(userId: string) {
const result = await client.action('send-welcome-email').execute({
userId: userId,
template: 'new-user-template-2024',
});
console.log(`Action completed with status: ${result.status}`);
return result;
}
This code doesn't just call an email API. It executes a managed, versioned, and auditable task. We know for certain whether that email was successfully sent or not. This is our first, perfect building block.
Sending one email is great, but our user onboarding process is more complex. It requires a sequence of actions:
Attempting to code this sequence manually in a single monolithic function is a recipe for disaster. What happens if provision-workspace fails after create-db-record has already succeeded? You now have an orphaned user record and a confused user who never got their workspace. Handling these failure states, retries, and compensation logic quickly turns simple code into an unmaintainable mess.
The solution is not to make the actions bigger, but to introduce a layer that understands how to connect them: an orchestrator.
This is where workflow.do comes in. It's an orchestration agent designed specifically to chain individual atomic actions together to form a larger, stateful business process.
Instead of writing imperative code that says "do this, then do that," you declaratively define the flow. The orchestrator is then responsible for executing each action.do task in the correct order, passing data between steps, and handling any errors along the way.
Let's model our user onboarding process with a hypothetical workflow.do SDK.
import { WorkflowDo, action } from '@do-platform/sdk';
const client = new WorkflowDo({ apiKey: 'YOUR_API_KEY' });
// Define a complex onboarding workflow by chaining atomic actions
const newUserOnboarding = client.workflow('new-user-onboarding')
.receives<{ email: string, name: string }>()
.step('create-user', action('create-db-record'))
.step('create-workspace', action('provision-workspace'), {
// Pass output from the previous step as input to the next
inputs: { userId: (ctx) => ctx.steps['create-user'].output.id }
})
.step('send-email', action('send-welcome-email'), {
inputs: { userId: (ctx) => ctx.steps['create-user'].output.id }
})
.step('notify-sales', action('notify-slack-channel'), {
inputs: {
channel: '#new-trials',
message: (ctx) => `New user signed up: ${ctx.trigger.name} (${ctx.trigger.email})`
}
});
// To execute the entire workflow:
async function runOnboarding(email: string, name: string) {
const result = await newUserOnboarding.execute({ email, name });
console.log(`Workflow completed with status: ${result.status}`);
}
This approach provides enormous benefits:
action.do and workflow.do are designed to work together to create powerful agentic workflows.
Think of it like building with LEGOs. action.do provides the perfectly molded, indestructible bricks. workflow.do provides the instruction manual that shows you how to connect them to build anything you can imagine, from a simple wall to an entire spaceship.
Ready to move beyond fragile scripts and build truly reliable automation? Start by defining your business processes as a chain of atomic actions.
Q: What is an 'atomic action'?
A: An atomic action is a single, indivisible operation that either completes successfully or fails entirely, leaving no partial state. This guarantees data integrity and reliability in your workflows.
Q: How is action.do different from a simple function call?
A: action.do provides a layer of abstraction with built-in observability, retries, and audit trails. It treats actions as manageable, versioned services, making your automation more robust and transparent than a standard function.
Q: Can actions be chained together?
A: Yes. action.do is designed as a fundamental building block. You can use an orchestration agent (like workflow.do) to chain multiple atomic actions together to create complex and powerful services.