QuantaSneaks is an AI-enabled shoe company famous for its x-factor worldwide. After the immense success of its predecessor, the revolutionary QuantaSneaks 1000 π is being launched in a weekend sale.
The social media campaign is set up, and there is no doubt that sales are going to be record high. However, the team is concerned that resellers might try to use bots to collect as many shoes as possible, and genuine customers might miss the deal.
To solve the issue, the lead architect decided to implement a human-in-the-loop workflow using newly launched shiny shiny durable lambda functions!

But what is this durable lambda?
AWS Durable Lambda Functions are like Step Functions inside a Lambda function. Durable Lambda execution spans across multiple Lambda invocations and uses a checkpoint-and-replay mechanism. It offers features like out-of-the-box infrastructure-level retries, concurrent code execution, and wait operations.
The wait operation is especially interesting here because it allows execution to pause (for up to one year) without incurring extra charges. Built on top of wait is the waitForCallback feature that we are going to use for human-in-the-loop workflows.
You can read more about Durable Lambda in Eric’s blog post.
What are we building?
The customer should be able to select the shoe size, place an order, and pay without any hassle. After payment, the order will remain on hold until someone from the fulfillment team approves it. A real human from the fulfillment team will use the AI-generated score to decide whether the order should be approved or not.
So the system must retry appropriately and gracefully restore the application to a consistent state in case of an unfixable error.
Key ingredients π§Ί
DynamoDB (the storage): Used to store orderId, paymentUrl, callback info, and approval status.
uiLambda (user-facing lambda): Serves static UI and handles APIs to complete payment, approve order, and fetch status.
orderLambda (trigger): Intermediate function to start order processing workflow (optionally verify user details).
durableLambda (the orchestrator): Analyzes risk score, orchestrates payment, and human approval.
Workflow π·πΌββοΈπ·πΌββοΈπ·πΌ
The workflow is as follows:
- Customer accesses the site URL and uiLambda serves the webpage.
- When an order is placed, the orderLambda is triggered.
- orderLambda creates orderId, stores it in DynamoDB, starts durable execution for orderId, and returns orderId to the customer.
- The customer’s browser polls uiLambda for the payment URL corresponding to the orderId.
- durableLambda uses AI to generate a risk score for an order and also calls the payment provider to generate the payment URL. Then, the
waitForCallbackoperation starts, details are stored in DynamoDB, and it waits for callback approval.
- The customer’s browser gets the payment URL, completes the payment by calling uiLambda. uiLambda approves the callback. An important thing here is that in production, the customer’s browser will talk to the payment provider, and the payment provider will resolve the callback.
- Then the browser polls for the status update.
- durableLambda starts a new
waitForCallbackfor human approval, and the status is updated in DynamoDB based on the decision. - Finally, the customer’s browser reflects the decision and status.

Note: Here, security features like Cognito and API Gateway are intentionally skipped to keep focus on the durable workflow.
Project Code π»
Complete code can be cloned from GitHub: https://github.com/TrickSumo/DurableLambdaCoruse/tree/main/8.%20QuantaSneaks%20Project
uiLambda:
import { handleHome } from './routes/home.js';
import { handleStatus, handlePoll } from './routes/status.js';
import { handlePayment, handlePay } from './routes/payment.js';
import { handleAdmin, handleApprove, handleReject } from './routes/admin.js';
import { serveAsset } from './lib/assets.js';
export const handler = async (event) => {
const { view, action, asset } = event.queryStringParameters ?? {};
const method = event.requestContext.http.method;
if (asset) return serveAsset(asset);
if (method === 'GET') {
if (action === 'poll') return await handlePoll(event);
if (!view) return handleHome();
if (view === 'status') return handleStatus(event);
if (view === 'payment') return handlePayment(event);
if (view === 'admin') return await handleAdmin();
}
if (method === 'POST') {
if (action === 'pay') return await handlePay(event);
if (action === 'approve') return await handleApprove(event);
if (action === 'reject') return await handleReject(event);
}
return { statusCode: 404, body: 'Not found' };
};
orderLambda:
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { randomUUID } from 'crypto';
const dynamo = new DynamoDBClient({});
const lambda = new LambdaClient({});
const TABLE = process.env.ORDERS_TABLE ?? 'quantasneaks';
const DURABLE_LAMBDA_ARN = process.env.DURABLE_LAMBDA_ARN;
const UI_LAMBDA_URL = process.env.UI_LAMBDA_URL;
function parseBody(event) {
const raw = event.isBase64Encoded
? Buffer.from(event.body, 'base64').toString('utf-8')
: event.body ?? '';
const contentType = event.headers?.['content-type'] ?? '';
if (contentType.includes('application/json')) return JSON.parse(raw);
return Object.fromEntries(new URLSearchParams(raw));
}
export const handler = async (event) => {
const body = parseBody(event);
const orderId = randomUUID();
const size = body.size ?? 'unknown';
await dynamo.send(new PutItemCommand({
TableName: TABLE,
Item: {
orderId: { S: orderId },
status: { S: 'processing' },
size: { S: size },
createdAt: { S: new Date().toISOString() }
}
}));
await lambda.send(new InvokeCommand({
FunctionName: DURABLE_LAMBDA_ARN,
InvocationType: 'Event',
Payload: Buffer.from(JSON.stringify({ orderId, size }))
}));
return {
statusCode: 302,
headers: { Location: `${UI_LAMBDA_URL}?view=status&orderId=${orderId}` }
};
};
durableLambda:
import { withDurableExecution } from '@aws/durable-execution-sdk-js';
import { updateOrder } from './lib/dynamo.js';
export const handler = withDurableExecution(async (event, context) => {
const { orderId } = event;
const compensations = [];
const analysis = await context.step('llm-analysis', () => callLLM(orderId));
try {
// Optional inventory reservation step could go here, with compensation to release inventory if payment isn't completed
const { paymentId } = await context.waitForCallback(
'payment',
async (callbackId) => {
await updateOrder(orderId, {
status: { S: 'awaiting_payment' },
riskLevel: { S: analysis.riskLevel },
riskReason: { S: analysis.reason },
paymentCallbackId: { S: callbackId }
});
},
{ timeout: { hours: 1 } }
);
compensations.push({
name: 'refund-payment',
fn: () => refundPayment(paymentId)
});
await context.waitForCallback(
'admin-approval',
async (callbackId) => {
await updateOrder(orderId, {
status: { S: 'awaiting_approval' },
approvalCallbackId: { S: callbackId }
});
},
{ timeout: { hours: 24 } }
);
await context.step('ship-order', () =>
updateOrder(orderId, { status: { S: 'shipped' } })
);
await context.step('send-confirmation', () => sendConfirmationEmail(orderId));
return { orderId, status: 'shipped' };
} catch (error) {
for (const comp of compensations.reverse()) {
await context.step(comp.name, () => comp.fn());
}
await context.step('compensate', () =>
updateOrder(orderId, { status: { S: 'compensated' } })
);
return { orderId, status: 'compensated' };
}
});
async function refundPayment(paymentId) {
console.log(`Refunding payment ${paymentId}`);
}
async function sendConfirmationEmail(orderId) {
console.log(`Confirmation email sent for order ${orderId}`);
}
async function callLLM(orderId) {
const levels = ['low', 'medium', 'high'];
const riskLevel = levels[Math.floor(Math.random() * levels.length)];
const reasons = {
low: 'Account history looks legitimate',
medium: 'New account with limited purchase history',
high: 'Multiple claim attempts detected from same origin'
};
return { riskLevel, reason: reasons[riskLevel] };
}
Conclusion
And that’s it!
We are now ready for the saleπ₯³
Hope this blog post gave you a good understanding of how to implement an e-commerce workflow with humans in the loop using Durable Lambda. If you have any feedback or suggestions, let’s discuss in the comments. Thanks!