<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: karthik balasubramanian</title>
    <description>The latest articles on DEV Community by karthik balasubramanian (@krtk_blsbrmnian).</description>
    <link>https://dev.to/krtk_blsbrmnian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3332432%2F38477ce3-15f0-47d4-8fb1-eaed332eae80.jpg</url>
      <title>DEV Community: karthik balasubramanian</title>
      <link>https://dev.to/krtk_blsbrmnian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/krtk_blsbrmnian"/>
    <language>en</language>
    <item>
      <title>Automating Receipt Processing with added convenience</title>
      <dc:creator>karthik balasubramanian</dc:creator>
      <pubDate>Mon, 14 Jul 2025 18:19:23 +0000</pubDate>
      <link>https://dev.to/krtk_blsbrmnian/automating-receipt-processing-with-added-convenience-2bpi</link>
      <guid>https://dev.to/krtk_blsbrmnian/automating-receipt-processing-with-added-convenience-2bpi</guid>
      <description>&lt;h6&gt;
  
  
  What we're going to do,
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97bn2fl11691gvi66yf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97bn2fl11691gvi66yf1.png" alt="Flow Diagram" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing and keeping track of receipts is always tedious and sometimes we tend to miss noting down the expenses what if we automate that process,&lt;/p&gt;

&lt;p&gt;that is what this project aims to do,&lt;/p&gt;

&lt;p&gt;you upload the receipt in you WhatsApp and let it take care of the rest and send you an E-mail&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1️⃣Create a twilio WhatsApp sandbox API,&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzfh9w6jcltbmkrhux0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzfh9w6jcltbmkrhux0z.png" alt="twilio whatsapp sandbox" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2️⃣Create a s3 bucket,&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dyc7ddp3osnizkhori8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dyc7ddp3osnizkhori8.png" alt="AWS s3" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3️⃣Create a webhook,&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from 'express';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import bodyParser from 'body-parser';
import dotenv from 'dotenv';
import fetch from 'node-fetch'; 

dotenv.config();

const app = express();
app.use(bodyParser.urlencoded({ extended: false }));

const s3 = new S3Client({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  },
});

app.post('/whatsapp-webhook', async (req, res) =&amp;gt; {
  const { Body, From, NumMedia, MediaUrl0, MediaContentType0 } = req.body;
  const sender = From.replace('whatsapp:', '');
  const timestamp = Date.now();

  try {
    if (parseInt(NumMedia) &amp;gt; 0) {
      const response = await fetch(MediaUrl0); // Twilio sends signed URL
      const buffer = await response.buffer();

      const extension = MediaContentType0.split('/')[1]; // e.g., image/jpeg → jpeg
      const Key = `whatsapp-media/${timestamp}-${sender}.${extension}`;

      await s3.send(new PutObjectCommand({
        Bucket: process.env.S3_BUCKET_NAME,
        Key,
        Body: buffer,
        ContentType: MediaContentType0,
      }));

      console.log(`✅ Media uploaded: ${Key}`);

      // Send reply back to user
      return res.send(`
        &amp;lt;Response&amp;gt;
          &amp;lt;Message&amp;gt;📁 Got your file! Uploaded as ${Key}&amp;lt;/Message&amp;gt;
        &amp;lt;/Response&amp;gt;
      `);
    } else {
      // Handle text messages
      const Key = `whatsapp-text/${timestamp}-${sender}.txt`;

      await s3.send(new PutObjectCommand({
        Bucket: process.env.S3_BUCKET_NAME,
        Key,
        Body,
        ContentType: 'text/plain',
      }));

      console.log(`✅ Text uploaded: ${Key}`);

      return res.send(`
        &amp;lt;Response&amp;gt;
          &amp;lt;Message&amp;gt;📝 Got your text and saved it!&amp;lt;/Message&amp;gt;
        &amp;lt;/Response&amp;gt;
      `);
    }
  } catch (err) {
    console.error('❌ Error:', err);
    res.status(500).send('&amp;lt;Response&amp;gt;&amp;lt;Message&amp;gt;😓 Something went wrong&amp;lt;/Message&amp;gt;&amp;lt;/Response&amp;gt;');
  }
});

app.listen(process.env.PORT, () =&amp;gt; {
  console.log(`Webhook ready at http://localhost:${process.env.PORT}`);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4️⃣Expose it using ngrok,&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node index.js
npm i -g ngrok
ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5️⃣Paste the ngrok url in twilio console,&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf0j0273izo5dqyutfjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flf0j0273izo5dqyutfjd.png" alt="twilio whatsapp" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6️⃣Create a DynamoDB table to store Data,&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3p8y1ynvr0q1bjr4fttg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3p8y1ynvr0q1bjr4fttg.png" alt="DynamoDB table" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7️⃣Set Up Amazon SES (to send emails)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a Identity in SES to send E-mails&lt;/p&gt;

&lt;p&gt;Assuming you're in a sandbox account add &amp;amp; verify the recipient E-mail as well for non-sandbox accounts this step is not needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhhp1uhc5rbku0ojpzxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhhp1uhc5rbku0ojpzxe.png" alt="SES create Identity" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After verifying your sender E-mail you must see something like this,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fptczkpai90vr4fs5ivko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fptczkpai90vr4fs5ivko.png" alt="SES sender verification" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8️⃣Create IAM Role for Lambda Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a new Role choose Lambda as the use case,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmsqmgm8xnww5lpkxj0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmsqmgm8xnww5lpkxj0c.png" alt="Lambda Role" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Attach the following policies,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   - `AmazonS3ReadOnlyAccess`
   - `AmazonTextractFullAccess`
   - `AmazonDynamoDBFullAccess`
   - `AmazonSESFullAccess`
   - `AWSLambdaBasicExecutionRole`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Name the role &lt;code&gt;LambdaReceiptProcessingRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktry8ymmghepdmrvtlkp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktry8ymmghepdmrvtlkp.png" alt="IAM Role" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9️⃣Create Lambda Function (processing engine)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Name the function &lt;code&gt;ProcessReceiptFunction&lt;/code&gt;&lt;br&gt;
Choose the existing role we just created,&lt;br&gt;
Runtime Choose &lt;code&gt;Python 3.9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyaxsg8k6gsiaagckmxtd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyaxsg8k6gsiaagckmxtd.png" alt="Lambda function Creation" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to Configuration&amp;gt; Environment Variables and add these variables,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuquisaaci36wghn99dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuquisaaci36wghn99dy.png" alt="Env Variables" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to the Code tab and paste this code,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import boto3
import uuid
from datetime import datetime
import urllib.parse

# Initialize AWS clients
s3 = boto3.client('s3')
textract = boto3.client('textract')
dynamodb = boto3.resource('dynamodb')
ses = boto3.client('ses')

# Environment variables
DYNAMODB_TABLE = os.environ.get('DYNAMODB_TABLE', 'Receipts')
SES_SENDER_EMAIL = os.environ.get('SES_SENDER_EMAIL', 'your-email@example.com')
SES_RECIPIENT_EMAIL = os.environ.get('SES_RECIPIENT_EMAIL', 'recipient@example.com')

def lambda_handler(event, context):
    try:
        # Get the S3 bucket and key from the event
        bucket = event['Records'][0]['s3']['bucket']['name']
        # URL decode the key to handle spaces and special characters
        key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'])

        print(f"Processing receipt from {bucket}/{key}")

        # Verify the object exists before proceeding
        try:
            s3.head_object(Bucket=bucket, Key=key)
            print(f"Object verification successful: {bucket}/{key}")
        except Exception as e:
            print(f"Object verification failed: {str(e)}")
            raise Exception(f"Unable to access object {key} in bucket {bucket}: {str(e)}")

        # Step 1: Process receipt with Textract
        receipt_data = process_receipt_with_textract(bucket, key)

        # Step 2: Store results in DynamoDB
        store_receipt_in_dynamodb(receipt_data, bucket, key)

        # Step 3: Send email notification
        send_email_notification(receipt_data)

        return {
            'statusCode': 200,
            'body': json.dumps('Receipt processed successfully!')
        }
    except Exception as e:
        print(f"Error processing receipt: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps(f'Error: {str(e)}')
        }

def process_receipt_with_textract(bucket, key):
    """Process receipt using Textract's AnalyzeExpense operation"""
    try:
        print(f"Calling Textract analyze_expense for {bucket}/{key}")
        response = textract.analyze_expense(
            Document={
                'S3Object': {
                    'Bucket': bucket,
                    'Name': key
                }
            }
        )
        print("Textract analyze_expense call successful")
    except Exception as e:
        print(f"Textract analyze_expense call failed: {str(e)}")
        raise

    # Generate a unique ID for this receipt
    receipt_id = str(uuid.uuid4())

    # Initialize receipt data dictionary
    receipt_data = {
        'receipt_id': receipt_id,
        'date': datetime.now().strftime('%Y-%m-%d'),  # Default date
        'vendor': 'Unknown',
        'total': '0.00',
        'items': [],
        's3_path': f"s3://{bucket}/{key}"
    }

    # Extract data from Textract response
    if 'ExpenseDocuments' in response and response['ExpenseDocuments']:
        expense_doc = response['ExpenseDocuments'][0]

        # Process summary fields (TOTAL, DATE, VENDOR)
        if 'SummaryFields' in expense_doc:
            for field in expense_doc['SummaryFields']:
                field_type = field.get('Type', {}).get('Text', '')
                value = field.get('ValueDetection', {}).get('Text', '')

                if field_type == 'TOTAL':
                    receipt_data['total'] = value
                elif field_type == 'INVOICE_RECEIPT_DATE':
                    # Try to parse and format the date
                    try:
                        receipt_data['date'] = value
                    except:
                        # Keep the default date if parsing fails
                        pass
                elif field_type == 'VENDOR_NAME':
                    receipt_data['vendor'] = value

        # Process line items
        if 'LineItemGroups' in expense_doc:
            for group in expense_doc['LineItemGroups']:
                if 'LineItems' in group:
                    for line_item in group['LineItems']:
                        item = {}
                        for field in line_item.get('LineItemExpenseFields', []):
                            field_type = field.get('Type', {}).get('Text', '')
                            value = field.get('ValueDetection', {}).get('Text', '')

                            if field_type == 'ITEM':
                                item['name'] = value
                            elif field_type == 'PRICE':
                                item['price'] = value
                            elif field_type == 'QUANTITY':
                                item['quantity'] = value

                        # Add to items list if we have a name
                        if 'name' in item:
                            receipt_data['items'].append(item)

    print(f"Extracted receipt data: {json.dumps(receipt_data)}")
    return receipt_data

def store_receipt_in_dynamodb(receipt_data, bucket, key):
    """Store the extracted receipt data in DynamoDB"""
    try:
        table = dynamodb.Table(DYNAMODB_TABLE)

        # Convert items to a format DynamoDB can store
        items_for_db = []
        for item in receipt_data['items']:
            items_for_db.append({
                'name': item.get('name', 'Unknown Item'),
                'price': item.get('price', '0.00'),
                'quantity': item.get('quantity', '1')
            })

        # Create item to insert
        db_item = {
            'receipt_id': receipt_data['receipt_id'],
            'date': receipt_data['date'],
            'vendor': receipt_data['vendor'],
            'total': receipt_data['total'],
            'items': items_for_db,
            's3_path': receipt_data['s3_path'],
            'processed_timestamp': datetime.now().isoformat()
        }

        # Insert into DynamoDB
        table.put_item(Item=db_item)
        print(f"Receipt data stored in DynamoDB: {receipt_data['receipt_id']}")
    except Exception as e:
        print(f"Error storing data in DynamoDB: {str(e)}")
        raise

def send_email_notification(receipt_data):
    """Send an email notification with receipt details"""
    try:
        # Format items for email
        items_html = ""
        for item in receipt_data['items']:
            name = item.get('name', 'Unknown Item')
            price = item.get('price', 'N/A')
            quantity = item.get('quantity', '1')
            items_html += f"&amp;lt;li&amp;gt;{name} - ${price} x {quantity}&amp;lt;/li&amp;gt;"

        if not items_html:
            items_html = "&amp;lt;li&amp;gt;No items detected&amp;lt;/li&amp;gt;"

        # Create email body
        html_body = f"""
        &amp;lt;html&amp;gt;
        &amp;lt;body&amp;gt;
            &amp;lt;h2&amp;gt;Hurray!! Completed My Automation Project&amp;lt;/h2&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Receipt ID:&amp;lt;/strong&amp;gt; {receipt_data['receipt_id']}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Vendor:&amp;lt;/strong&amp;gt; {receipt_data['vendor']}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Date:&amp;lt;/strong&amp;gt; {receipt_data['date']}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Total Amount:&amp;lt;/strong&amp;gt; ${receipt_data['total']}&amp;lt;/p&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;S3 Location:&amp;lt;/strong&amp;gt; {receipt_data['s3_path']}&amp;lt;/p&amp;gt;

            &amp;lt;h3&amp;gt;Items:&amp;lt;/h3&amp;gt;
            &amp;lt;ul&amp;gt;
                {items_html}
            &amp;lt;/ul&amp;gt;

            &amp;lt;p&amp;gt;The receipt has been processed and stored in DynamoDB.&amp;lt;/p&amp;gt;
        &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
        """

        # Send email using SES
        ses.send_email(
            Source=SES_SENDER_EMAIL,
            Destination={
                'ToAddresses': [SES_RECIPIENT_EMAIL]
            },
            Message={
                'Subject': {
                    'Data': f"Receipt Processed: {receipt_data['vendor']} - ${receipt_data['total']}"
                },
                'Body': {
                    'Html': {
                        'Data': html_body
                    }
                }
            }
        )

        print(f"Email notification sent to {SES_RECIPIENT_EMAIL}")
    except Exception as e:
        print(f"Error sending email notification: {str(e)}")
        # Continue execution even if email fails
        print("Continuing execution despite email error")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to configuration tab &amp;gt; General configuration &amp;gt; edit&lt;br&gt;
Increase the timeout from 0.3 sec to 2 min for complex file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feupvexuf7xr2kdpobrmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feupvexuf7xr2kdpobrmc.png" alt="Edit settings" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hit save&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1️⃣0️⃣Again go to the s3&lt;/strong&gt;&lt;br&gt;
In the Properties Tab&lt;br&gt;
Add the Event Notification&lt;br&gt;
Prefix : whatsapp-media/&lt;br&gt;
Object creation : Select All object create events&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dhubrwdacvk6h9ffbeo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dhubrwdacvk6h9ffbeo.png" alt="Event trigger creation" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1️⃣1️⃣Finally choose the Destination&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzf0p6scf2l37r7hlobo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzf0p6scf2l37r7hlobo.png" alt="Destination" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wait for 30 sec and also check in spam folder for the mail....if you do not receive mail after 2 min go to the monitor tab in Lambda Function and check the log groups in cloudwatch&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagjlw0q8kd4etxnah2kh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagjlw0q8kd4etxnah2kh.png" alt="Cloudwatch logs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>automation</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
