Skip to content

Commit 0635655

Browse files
authored
Merge pull request #2874 from agawanea/lambda-durable-human-approval-sam
New Serverless Pattern - Add Lambda Durable Functions - Human Approval Pattern
2 parents 68cb53b + d0d1dc9 commit 0635655

File tree

8 files changed

+839
-0
lines changed

8 files changed

+839
-0
lines changed
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# Human-in-the-Loop Approval Workflow with AWS Lambda durable functions
2+
3+
This pattern demonstrates a human-in-the-loop approval workflow using AWS Lambda durable functions. The workflow pauses execution for up to 24 hours while waiting for human approval via email, automatically handling timeouts and resuming when decisions are received.
4+
5+
**Important:** Please check the [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html) for regions currently supported by AWS Lambda durable functions.
6+
7+
Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-hitl-approval-sam
8+
9+
## Architecture
10+
11+
![Architecture Diagram](architecture.png)
12+
13+
The pattern uses Lambda durable functions to implement a cost-effective approval workflow that can wait up to 24 hours without incurring compute charges during the wait period.
14+
15+
### Workflow Steps
16+
17+
1. **User submits approval request** via Amazon API Gateway
18+
2. **Durable Function validates request** and creates a callback
19+
3. **UUID mapping stored in Amazon DynamoDB** (callback ID → UUID)
20+
4. **Email notification sent via SNS** with approve/reject links
21+
5. **Function pauses execution** (no compute charges during wait)
22+
6. **Approver clicks link** in email
23+
7. **Callback Handler retrieves callback ID** from DynamoDB
24+
8. **Callback sent to Lambda** using `SendDurableExecutionCallbackSuccess`
25+
9. **Durable Function resumes** and processes the decision
26+
27+
## Key Features
28+
29+
-**24-Hour Wait Time** - Can wait up to 24 hours for approval
30+
-**No Compute Charges During Wait** - Function suspended during wait period
31+
-**Email Notifications** - Sends approval requests with clickable links
32+
-**Short, Clean URLs** - Uses UUID instead of long callback IDs
33+
-**DynamoDB Mapping** - Stores UUID → callback ID mapping with TTL
34+
-**Automatic Timeout** - Auto-rejects after 24 hours
35+
-**HTML Response Pages** - Beautiful approval/rejection confirmation pages
36+
37+
## Prerequisites
38+
39+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
40+
* [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) installed
41+
* Python 3.14 runtime (automatically provided by Lambda)
42+
43+
## Deployment
44+
45+
1. Navigate to the pattern directory:
46+
```bash
47+
cd lambda-durable-hitl-approval-sam
48+
```
49+
50+
2. Build the SAM application:
51+
```bash
52+
sam build
53+
```
54+
55+
3. Deploy the application (must use us-east-2 region):
56+
```bash
57+
sam deploy --guided --region us-east-2
58+
```
59+
60+
During the guided deployment, provide the required values:
61+
- **ApproverEmail**: Enter your email address to receive approval notifications
62+
- Allow SAM CLI to create IAM roles when prompted
63+
64+
4. **Confirm SNS subscription**: Check your email and click the confirmation link from Amazon SNS
65+
66+
5. Note the `ApiEndpoint` from the outputs
67+
68+
## Testing
69+
70+
### Submit an Approval Request
71+
72+
```bash
73+
curl -X POST https://YOUR-API-ID.execute-api.us-east-2.amazonaws.com/prod/requests \
74+
-H "Content-Type: application/json" \
75+
-d '{
76+
"requestId": "REQ-001",
77+
"amount": 5000,
78+
"description": "New server purchase"
79+
}'
80+
```
81+
82+
Response:
83+
```json
84+
{
85+
"message": "Request accepted"
86+
}
87+
```
88+
89+
### Check Your Email
90+
91+
You'll receive an email with:
92+
- Request details (ID, amount, description)
93+
- **APPROVE** link
94+
- **REJECT** link
95+
- Expiration time (24 hours)
96+
97+
### Click Approve or Reject
98+
99+
Click one of the links in the email. You'll see a confirmation page that displays a checkmark or X icon along with either a "Request Approved!" or "Request Rejected!" message. The page will also confirm that the workflow has been notified of your decision.
100+
101+
102+
103+
## How It Works
104+
105+
### Callback Creation
106+
107+
The durable function creates a callback and stores the mapping:
108+
109+
```python
110+
from aws_durable_execution_sdk_python import DurableContext, durable_execution
111+
from aws_durable_execution_sdk_python.config import CallbackConfig, Duration
112+
113+
@durable_execution
114+
def lambda_handler(event, context: DurableContext):
115+
# Create callback with 24-hour timeout
116+
callback = context.create_callback(
117+
name='wait-for-approval',
118+
config=CallbackConfig(timeout=Duration.from_hours(24))
119+
)
120+
121+
# Generate short UUID and store mapping in DynamoDB
122+
request_uuid = str(uuid.uuid4())
123+
table.put_item(Item={
124+
'uuid': request_uuid,
125+
'callbackId': callback.callback_id,
126+
'ttl': int(time.time()) + 86400 # 24 hours
127+
})
128+
129+
# Send email with UUID-based URLs
130+
approve_url = f"{api_base_url}/approve/{request_uuid}"
131+
132+
# Wait for callback result
133+
approval = callback.result()
134+
```
135+
136+
### Callback Handler
137+
138+
When user clicks approve/reject:
139+
140+
```python
141+
def lambda_handler(event, context):
142+
# Get UUID from URL path
143+
request_uuid = event['pathParameters']['uuid']
144+
145+
# Fetch callback ID from DynamoDB
146+
response = table.get_item(Key={'uuid': request_uuid})
147+
callback_id = response['Item']['callbackId']
148+
149+
# Send callback to durable function
150+
lambda_client.send_durable_execution_callback_success(
151+
CallbackId=callback_id,
152+
Result=json.dumps({'decision': 'approved'})
153+
)
154+
```
155+
156+
157+
## Configuration
158+
159+
### Adjust Timeout Duration
160+
161+
To change the timeout duration, you need to update both the Lambda function configuration and the callback configuration:
162+
163+
Modify in `template.yaml`:
164+
165+
```yaml
166+
DurableConfig:
167+
ExecutionTimeout: 86400 # 24 hours in seconds
168+
```
169+
170+
And in `src/lambda_function.py`:
171+
172+
```python
173+
config=CallbackConfig(timeout=Duration.from_hours(24))
174+
```
175+
176+
Both values must match: `ExecutionTimeout` sets the maximum runtime for the durable function, while `CallbackConfig.timeout` sets how long the callback will wait before timing out.
177+
178+
### Change Email Address
179+
180+
Update during deployment or redeploy with new email:
181+
182+
```bash
183+
sam deploy --parameter-overrides ApproverEmail=new-email@example.com
184+
```
185+
186+
## Use Cases
187+
188+
- **Expense Approvals** - Wait for manager approval on spending requests
189+
- **Document Reviews** - Pause workflow while documents are reviewed
190+
- **Deployment Approvals** - Require human approval before production deployments
191+
- **Access Requests** - Pause while security team reviews access requests
192+
- **Contract Approvals** - Wait for legal team approval on contracts
193+
194+
## Monitoring
195+
196+
### CloudWatch Logs
197+
198+
Monitor the durable function:
199+
```bash
200+
aws logs tail /aws/lambda/lambda-durable-hitl-approval-ApprovalFunction-XXXXX \
201+
--region us-east-2 \
202+
--follow
203+
```
204+
205+
Monitor the callback handler:
206+
```bash
207+
aws logs tail /aws/lambda/lambda-durable-hitl-approv-CallbackHandlerFunction-XXXXX \
208+
--region us-east-2 \
209+
--follow
210+
```
211+
212+
213+
214+
## Cleanup
215+
216+
```bash
217+
sam delete --stack-name lambda-durable-hitl-approval --region us-east-2
218+
```
219+
220+
221+
## Learn More
222+
223+
- [Lambda durable functions Documentation](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html)
224+
- [Durable Execution SDK (Python)](https://github.com/aws/aws-durable-execution-sdk-python)
225+
- [Callback Operations](https://docs.aws.amazon.com/lambda/latest/dg/durable-callback.html)
226+
- [SendDurableExecutionCallbackSuccess API](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda/client/send_durable_execution_callback_success.html)
227+
228+
---
229+
230+
Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
231+
232+
SPDX-License-Identifier: MIT-0
118 KB
Loading
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"title": "Human-in-the-Loop Approval Workflow with Lambda durable functions",
3+
"description": "Approval workflow that pauses execution while waiting for human decisions, with automatic timeout handling and callback-based resumption",
4+
"language": "Python",
5+
"level": "300",
6+
"framework": "AWS SAM",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern demonstrates a human-in-the-loop approval workflow using Lambda durable functions with callback operations.",
11+
"The workflow pauses execution using create_callback() while waiting for human approval, incurring no compute charges during the wait period.",
12+
"If no decision is received within 24 hours, the workflow automatically times out and rejects the request.",
13+
"When an approver submits a decision via API, the durable function resumes from its checkpoint and processes the result.",
14+
"The pattern uses DynamoDB to map short UUIDs to callback IDs for clean approval URLs, and SNS to send email notifications with approve/reject links."
15+
]
16+
},
17+
"gitHub": {
18+
"template": {
19+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-human-approval-sam",
20+
"templateURL": "serverless-patterns/lambda-durable-human-approval-sam",
21+
"projectFolder": "lambda-durable-human-approval-sam",
22+
"templateFile": "template.yaml"
23+
}
24+
},
25+
"resources": {
26+
"bullets": [
27+
{
28+
"text": "Lambda durable functions Documentation",
29+
"link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html"
30+
},
31+
{
32+
"text": "Durable Execution SDK for Python",
33+
"link": "https://github.com/aws/aws-durable-execution-sdk-python"
34+
}
35+
]
36+
},
37+
"deploy": {
38+
"text": [
39+
"Note: Lambda durable functions have limited regional availability. Please check the AWS documentation for current regional support.",
40+
"sam build",
41+
"sam deploy --guided --region us-east-2"
42+
]
43+
},
44+
"testing": {
45+
"text": [
46+
"See the GitHub repo for detailed testing instructions."
47+
]
48+
},
49+
"cleanup": {
50+
"text": [
51+
"Delete the stack: <code>sam delete --region us-east-2</code>."
52+
]
53+
},
54+
"authors": [
55+
{
56+
"name": "Abhishek Agawane",
57+
"image": "https://drive.google.com/file/d/1E-5koDaKEaMUtOctX32I9TLwfh3kgpAq/view?usp=drivesdk",
58+
"bio": "Abhishek Agawane is a Security Consultant at Amazon Web Services with more than 8 years of industry experience. He helps organizations architect resilient, secure, and efficient cloud environments, guiding them through complex challenges and large-scale infrastructure transformations. He has helped numerous organizations enhance their cloud operations through targeted optimizations, robust architectures, and best-practice implementations.",
59+
"linkedin": "agawabhi"
60+
}
61+
]
62+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"title": "Human-in-the-Loop Approval Workflow with Lambda durable functions",
3+
"description": "Approval workflow that pauses execution while waiting for human decisions, with automatic timeout handling and callback-based resumption",
4+
"language": "Python",
5+
"level": "300",
6+
"framework": "AWS SAM",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern demonstrates a human-in-the-loop approval workflow using Lambda durable functions with callback operations.",
11+
"The workflow pauses execution using create_callback() while waiting for human approval, incurring no compute charges during the wait period.",
12+
"If no decision is received within 24 hours, the workflow automatically times out and rejects the request.",
13+
"When an approver submits a decision via API, the durable function resumes from its checkpoint and processes the result.",
14+
"The pattern uses DynamoDB to map short UUIDs to callback IDs for clean approval URLs, and SNS to send email notifications with approve/reject links."
15+
]
16+
},
17+
"gitHub": {
18+
"template": {
19+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-durable-human-approval-sam",
20+
"templateURL": "serverless-patterns/lambda-durable-human-approval-sam",
21+
"projectFolder": "lambda-durable-human-approval-sam",
22+
"templateFile": "template.yaml"
23+
}
24+
},
25+
"resources": {
26+
"bullets": [
27+
{
28+
"text": "Lambda durable functions Documentation",
29+
"link": "https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html"
30+
},
31+
{
32+
"text": "Durable Execution SDK for Python",
33+
"link": "https://github.com/aws/aws-durable-execution-sdk-python"
34+
}
35+
]
36+
},
37+
"deploy": {
38+
"text": [
39+
"Note: Lambda durable functions have limited regional availability. Please check the AWS documentation for current regional support.",
40+
"sam build",
41+
"sam deploy --guided --region us-east-2"
42+
]
43+
},
44+
"testing": {
45+
"text": [
46+
"See the GitHub repo for detailed testing instructions."
47+
]
48+
},
49+
"cleanup": {
50+
"text": [
51+
"Delete the stack: <code>sam delete --region us-east-2</code>."
52+
]
53+
},
54+
"authors": [
55+
{
56+
"name": "Abhishek Agawane",
57+
"image": "https://drive.google.com/file/d/1E-5koDaKEaMUtOctX32I9TLwfh3kgpAq/view?usp=drivesdk",
58+
"bio": "Abhishek Agawane is a Security Consultant at Amazon Web Services with more than 8 years of industry experience. He helps organizations architect resilient, secure, and efficient cloud environments, guiding them through complex challenges and large-scale infrastructure transformations. He has helped numerous organizations enhance their cloud operations through targeted optimizations, robust architectures, and best-practice implementations.",
59+
"linkedin": "agawabhi"
60+
}
61+
],
62+
"patternArch": {
63+
"icon1": {
64+
"x": 15,
65+
"y": 50,
66+
"service": "apigw",
67+
"label": "Amazon API Gateway"
68+
},
69+
"icon2": {
70+
"x": 50,
71+
"y": 50,
72+
"service": "lambda",
73+
"label": "AWS Lambda durable functions"
74+
},
75+
"icon3": {
76+
"x": 85,
77+
"y": 50,
78+
"service": "dynamodb",
79+
"label": "Amazon DynamoDB"
80+
},
81+
"line1": {
82+
"from": "icon1",
83+
"to": "icon2",
84+
"label": ""
85+
},
86+
"line2": {
87+
"from": "icon2",
88+
"to": "icon3",
89+
"label": ""
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)