Build an AI-Powered Lead Qualification API

Deploy a serverless API that uses Claude AI to score and qualify leads.

Introduction

30 Second Summary

Every business loses potential customers because inquiries sit too long without a response. The difference between a sale and a lost lead often comes down to how fast and how well you qualify that first contact.

In this project, you will build an AI-powered API that instantly qualifies incoming leads for a medspa business. Your API scores each inquiry by urgency and generates personalized follow-up messages.

What You'll Build

You'll create a serverless API powered by Claude Haiku 4.5 that qualifies incoming lead inquiries and stores structured AI analysis in DynamoDB.

By the end of this project, you'll have:

  • A Python Lambda function that integrates with the Claude Messages API to perform AI-powered lead qualification.
  • A structured prompt that extracts business intelligence (lead score, urgency, service match, follow-up message) from raw inquiry text.
  • A live REST API endpoint via API Gateway that accepts lead data and returns AI analysis in real-time.
  • Secret Mission: Build a batch Revenue Leak Report that identifies at-risk leads and recommends recovery actions.

Are there any prerequisites?

This project assumes you have an AWS account with the AWS CLI configured, an Anthropic API account, and Python 3 installed locally.

Basic familiarity with AWS Lambda and API Gateway from a prior project is ideal.

Set Up Your Environment and API Key

Before you can build the lead qualification logic, you need the infrastructure to support it. That means a working connection to Claude's API, a database to store qualified leads, and a Lambda function to run your code in the cloud.

In this step, you will set up all three foundations: your local development environment with the Anthropic SDK, a DynamoDB table for lead storage, and an AWS Lambda function ready to receive your code.

In this step, get ready to:
  • Generate your Anthropic API key and verify it works with a test call.
  • Create a DynamoDB table for storing qualified leads.
  • Create a Lambda function with the right permissions and environment variables.
Generate your Anthropic API key and test it locally

You need an API key to authenticate your requests to Claude. You will generate one in the Anthropic Console, then build a small local project to verify the connection works before deploying anything to AWS.

  • Open your browser and navigate to the Anthropic Console at console.anthropic.com.
  • Click API Keys in the left sidebar (or navigate to Settings > API Keys).
  • Click Create Key.
  • Give your key a name like lead-qualification and click Create Key.
  • Copy the key immediately and save it somewhere safe. You will not be able to see it again.

Keep your API key safe

Never share your API key or commit it to version control. You will paste it into your terminal as an environment variable and later into the Lambda configuration. If you lose it, you can always generate a new one from the Console.

Now set up a local project directory to test the connection.

  • Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows) and searching for Terminal.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir lead-qualification-api
cd lead-qualification-api
  • Create a Python virtual environment by running:
python3 -m venv venv
  • Activate the virtual environment by running:

macOS

source venv/bin/activate

Windows

venv\Scripts\activate

You should see (venv) appear at the beginning of your terminal prompt.

  • Install the Anthropic SDK by running:
pip install anthropic

What is the anthropic SDK?

The anthropic package is the official Python library for calling Claude's API. It handles authentication, request formatting, and response parsing so you can focus on writing prompts and processing results.

Now test the connection with a quick API call. You need to export your API key as an environment variable first.

  • Set your API key as an environment variable by running:
export ANTHROPIC_API_KEY="your-api-key-here"
  • Replace your-api-key-here with the key you copied from the Anthropic Console.
  • Run a quick test call to Claude Haiku 4.5 by running:
python3 -c "
from anthropic import Anthropic
client = Anthropic()
message = client.messages.create(
    model='claude-haiku-4-5',
    max_tokens=100,
    messages=[{'role': 'user', 'content': 'Say hello in one sentence.'}]
)
print(message.content[0].text)
"

What does this test do?

This one-liner creates an Anthropic client (which automatically reads your ANTHROPIC_API_KEY environment variable), sends a simple message to the claude-haiku-4-5 model, and prints the response. If you see a friendly greeting, your API key is working correctly.

You should see Claude respond with a short greeting in your terminal.

Seeing an authentication error?

Make sure you copied the full API key without any extra spaces. Run echo $ANTHROPIC_API_KEY to check the value is set correctly. If you see an empty result, re-run the export command with your key.

Help me debug my Anthropic API authentication error.

Create the DynamoDB table for storing qualified leads

Every lead that Claude qualifies needs to be stored somewhere for the sales team to review later. You will create a DynamoDB table that stores each qualified lead with its AI analysis.

  • Open the AWS Console in your browser and search for DynamoDB in the search bar at the top.
  • Click DynamoDB to open the DynamoDB dashboard.
  • Click Create table.
  • For Table name, enter QualifiedLeads.
  • For Partition key, enter lead_id and leave the type as String.
  • Leave all other settings at their defaults.
  • Click Create table.

Wait for the table status to change to Active. This usually takes about 10-15 seconds.

Why a partition key called lead_id?

DynamoDB needs a unique identifier for each item. Your Lambda function will generate a UUID for every new lead inquiry, stored as lead_id. This lets you look up any individual lead's qualification results later.

Table stuck in Creating status?

Refresh the page after 30 seconds. If it still shows Creating, check that you are in the same AWS region you normally use. You can see your region in the top-right corner of the console.

Help me troubleshoot my DynamoDB table creation.

Create the Lambda function with DynamoDB permissions

Your Lambda function is the compute layer that will run your lead qualification code. You need to create it with the right runtime and give it permission to write to the DynamoDB table you just created.

  • In the AWS Console search bar, search for Lambda and open the Lambda dashboard.
  • Click Create function.
  • Select Author from scratch.
  • For Function name, enter LeadQualificationFunction.
  • For Runtime, select Python 3.13.
  • Leave architecture set to x86_64.
  • Expand Change default execution role and select Create a new role with basic Lambda permissions.
  • Click Create function.

Lambda creates your function with a default execution role. Now you need to add DynamoDB permissions to that role.

  • On your function's page, click the Configuration tab.
  • Click Permissions in the left sidebar.
  • Under Execution role, click the role name link. This opens the IAM console in a new tab.
  • In the IAM console, click Add permissions, then Attach policies.
  • Search for AmazonDynamoDBFullAccess.
  • Select the checkbox next to AmazonDynamoDBFullAccess and click Add permissions.

Why DynamoDB full access?

For a portfolio project, the managed policy AmazonDynamoDBFullAccess is the fastest way to grant read/write access. In production, you would create a custom policy scoped to only the QualifiedLeads table for tighter security.

Now configure the environment variables your Lambda function needs.

  • Switch back to the Lambda console tab with your function.
  • Click the Configuration tab, then click Environment variables in the left sidebar.
  • Click Edit.
  • Click Add environment variable.
  • For the first variable, enter ANTHROPIC_API_KEY as the key and paste your Anthropic API key as the value.
  • Click Add environment variable again.
  • For the second variable, enter DYNAMODB_TABLE_NAME as the key and QualifiedLeads as the value.
  • Click Save.

Environment variables for API keys

Storing your API key as a Lambda environment variable keeps it out of your source code. For production workloads, AWS Secrets Manager is the recommended approach. For a portfolio demo, environment variables are perfectly acceptable.

Can't find the Environment variables section?

Make sure you are on the Configuration tab (not the Code tab). The Environment variables option appears in the left sidebar under Configuration.

Help me find the environment variables settings for my Lambda function.

Your environment is fully set up. You have a working API key, a database ready to store leads, and a Lambda function with the right permissions. Next up, you will write the prompt and handler code that turns raw lead inquiries into structured qualifications.

Build the Lead Qualification Lambda Function

Your Lambda function and DynamoDB table are ready. Now it's time to write the actual code that powers the lead qualification.

In this step, you'll build a structured prompt that teaches Claude how to score leads, then write the Python function that calls Claude's API and stores results in DynamoDB.

In this step, get ready to:
  • Write a system prompt that instructs Claude to return structured lead qualification data.
  • Build a Lambda handler that calls the Claude API and stores results in DynamoDB.
  • Package and deploy the function code to your Lambda.
Write the system prompt for lead qualification

The system prompt is the most important piece of this project. It tells Claude exactly how to analyze a lead inquiry and what structured data to return.

  • In your code editor, create a new file called lambda_function.py inside your lead-qualification-api/ folder.
  • Add the imports and client initialization by pasting this code:
import json
import os
import uuid
import time
import boto3
from anthropic import Anthropic

# Initialize clients
anthropic_client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ.get("DYNAMODB_TABLE_NAME", "QualifiedLeads"))

What does this code do?

These lines set up the two external services your function needs. The Anthropic client connects to Claude's API using the key stored in your Lambda environment variable.

The boto3.resource("dynamodb") call creates a connection to DynamoDB. The table variable references your QualifiedLeads table by name.

  • Below the client initialization, add the system prompt constant:
SYSTEM_PROMPT = """You are an expert lead qualification specialist for a premium medspa and wellness clinic. Your job is to analyze incoming lead inquiries and provide structured qualification data that helps the sales team prioritize follow-up.

For every lead inquiry, analyze the text and return a JSON object with exactly these fields:

{
  "lead_score": <integer 1-10>,
  "urgency": "<hot|warm|cold>",
  "service_match": "<best matching service category>",
  "buyer_signals": ["<signal 1>", "<signal 2>"],
  "follow_up_message": "<personalized follow-up message under 100 words>",
  "reasoning": "<brief explanation of your scoring>"
}

Scoring guidelines:
- 8-10 (Hot): Ready to book, mentions specific service, urgency language, price is secondary
- 5-7 (Warm): Interested but comparing options, asking about pricing, general curiosity
- 1-4 (Cold): Vague interest, just browsing, no specific service mentioned, price-first language

Service categories: Injectables, Body Contouring, Skin Rejuvenation, Laser Treatments, Wellness IV, Consultation Only

Return ONLY the JSON object, no additional text."""

Why is the prompt designed this way?

This prompt uses three key techniques. First, it assigns Claude a specific role (lead qualification specialist) so it applies domain expertise. Second, it defines an exact JSON schema so the output is always machine-parseable.

Third, it provides scoring guidelines that mirror how a real sales team would prioritize leads. The "Return ONLY the JSON" instruction prevents Claude from adding conversational text that would break your JSON parser.

  • Save lambda_function.py.

Seeing a syntax error in your editor?

Make sure the triple quotes """ at the start and end of the prompt string are on their own lines. Python multiline strings require matching triple-quote delimiters.

Help me fix a syntax error in my SYSTEM_PROMPT multiline string in lambda_function.py.

Write the Lambda handler functions

Your Lambda needs three functions. One calls Claude, one stores the result, and one orchestrates the full request lifecycle.

  • Below the SYSTEM_PROMPT constant, add the qualify_lead function:
def qualify_lead(inquiry_text, lead_source="unknown"):
    """Send a lead inquiry to Claude for qualification."""
    message = anthropic_client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=500,
        system=SYSTEM_PROMPT,
        messages=[
            {
                "role": "user",
                "content": f"Qualify this lead inquiry:\n\nSource: {lead_source}\nInquiry: {inquiry_text}",
            }
        ],
    )

    response_text = message.content[0].text
    qualification = json.loads(response_text)
    return qualification

What does this function do?

This function sends the lead inquiry to Claude Haiku 4.5 using the Messages API. The system parameter passes your scoring prompt, while the messages list contains the actual lead inquiry text.

Claude returns a JSON string, which json.loads() parses into a Python dictionary. The max_tokens=500 cap keeps responses concise and costs low.

  • Below qualify_lead, add the store_lead function:
def store_lead(lead_id, inquiry_text, lead_source, qualification):
    """Store the qualified lead in DynamoDB."""
    table.put_item(
        Item={
            "lead_id": lead_id,
            "inquiry_text": inquiry_text,
            "lead_source": lead_source,
            "lead_score": qualification["lead_score"],
            "urgency": qualification["urgency"],
            "service_match": qualification["service_match"],
            "buyer_signals": qualification["buyer_signals"],
            "follow_up_message": qualification["follow_up_message"],
            "reasoning": qualification["reasoning"],
            "timestamp": int(time.time()),
        }
    )

What does this function do?

This writes the full lead record to your DynamoDB table. Each field from Claude's qualification response is stored as its own attribute, making it easy to query and filter leads later.

The timestamp field records when the lead was qualified. This uses Unix epoch time, which is compact and easy to sort.

  • Below store_lead, add the main lambda_handler function:
def lambda_handler(event, context):
    """Main Lambda handler for lead qualification."""
    try:
        body = json.loads(event.get("body", "{}"))
        inquiry_text = body.get("inquiry")
        lead_source = body.get("source", "unknown")

        if not inquiry_text:
            return {
                "statusCode": 400,
                "headers": {"Content-Type": "application/json"},
                "body": json.dumps({"error": "Missing 'inquiry' field in request body"}),
            }

        lead_id = str(uuid.uuid4())
        qualification = qualify_lead(inquiry_text, lead_source)
        store_lead(lead_id, inquiry_text, lead_source, qualification)

        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps(
                {
                    "lead_id": lead_id,
                    "qualification": qualification,
                }
            ),
        }

    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Invalid JSON in request body"}),
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": f"Internal error: {str(e)}"}),
        }

What does this function do?

This is the entry point that AWS Lambda calls when a request arrives. It parses the incoming JSON body, validates that an inquiry field exists, then runs the full qualification pipeline.

The function returns properly formatted API Gateway responses with status codes, headers, and JSON bodies. The try/except blocks handle malformed input and unexpected errors gracefully.

  • Save lambda_function.py.

Seeing indentation warnings?

Python is strict about indentation. Make sure each level uses exactly 4 spaces (not tabs). The try block contents should be indented once from lambda_handler, and the if not inquiry_text block should be indented twice.

Help me fix indentation issues in my lambda_function.py file.

✔️ Awesome, I've got everything!

Great. Double check you've saved lambda_function.py before moving on to packaging.

ⓧ I'd like to double check the full code

Here is the complete lambda_function.py file. Make sure yours matches exactly:

import json
import os
import uuid
import time
import boto3
from anthropic import Anthropic

# Initialize clients
anthropic_client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ.get("DYNAMODB_TABLE_NAME", "QualifiedLeads"))

SYSTEM_PROMPT = """You are an expert lead qualification specialist for a premium medspa and wellness clinic. Your job is to analyze incoming lead inquiries and provide structured qualification data that helps the sales team prioritize follow-up.

For every lead inquiry, analyze the text and return a JSON object with exactly these fields:

{
  "lead_score": <integer 1-10>,
  "urgency": "<hot|warm|cold>",
  "service_match": "<best matching service category>",
  "buyer_signals": ["<signal 1>", "<signal 2>"],
  "follow_up_message": "<personalized follow-up message under 100 words>",
  "reasoning": "<brief explanation of your scoring>"
}

Scoring guidelines:
- 8-10 (Hot): Ready to book, mentions specific service, urgency language, price is secondary
- 5-7 (Warm): Interested but comparing options, asking about pricing, general curiosity
- 1-4 (Cold): Vague interest, just browsing, no specific service mentioned, price-first language

Service categories: Injectables, Body Contouring, Skin Rejuvenation, Laser Treatments, Wellness IV, Consultation Only

Return ONLY the JSON object, no additional text."""


def qualify_lead(inquiry_text, lead_source="unknown"):
    """Send a lead inquiry to Claude for qualification."""
    message = anthropic_client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=500,
        system=SYSTEM_PROMPT,
        messages=[
            {
                "role": "user",
                "content": f"Qualify this lead inquiry:\n\nSource: {lead_source}\nInquiry: {inquiry_text}",
            }
        ],
    )

    response_text = message.content[0].text
    qualification = json.loads(response_text)
    return qualification


def store_lead(lead_id, inquiry_text, lead_source, qualification):
    """Store the qualified lead in DynamoDB."""
    table.put_item(
        Item={
            "lead_id": lead_id,
            "inquiry_text": inquiry_text,
            "lead_source": lead_source,
            "lead_score": qualification["lead_score"],
            "urgency": qualification["urgency"],
            "service_match": qualification["service_match"],
            "buyer_signals": qualification["buyer_signals"],
            "follow_up_message": qualification["follow_up_message"],
            "reasoning": qualification["reasoning"],
            "timestamp": int(time.time()),
        }
    )


def lambda_handler(event, context):
    """Main Lambda handler for lead qualification."""
    try:
        body = json.loads(event.get("body", "{}"))
        inquiry_text = body.get("inquiry")
        lead_source = body.get("source", "unknown")

        if not inquiry_text:
            return {
                "statusCode": 400,
                "headers": {"Content-Type": "application/json"},
                "body": json.dumps({"error": "Missing 'inquiry' field in request body"}),
            }

        lead_id = str(uuid.uuid4())
        qualification = qualify_lead(inquiry_text, lead_source)
        store_lead(lead_id, inquiry_text, lead_source, qualification)

        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps(
                {
                    "lead_id": lead_id,
                    "qualification": qualification,
                }
            ),
        }

    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Invalid JSON in request body"}),
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": f"Internal error: {str(e)}"}),
        }
Package and deploy the Lambda function

Lambda doesn't include the anthropic SDK by default, so you need to bundle it alongside your code in a deployment package. This is a zip file containing everything your function needs to run.

Why package dependencies separately?

The Lambda runtime includes boto3 (the AWS SDK) by default, but third-party libraries like anthropic must be included in your deployment package. You install them into a local directory and zip everything together.

  • In your terminal, make sure you are inside the lead-qualification-api/ directory.
  • Install the anthropic SDK into a local package/ directory by running this command:
pip install --target ./package anthropic

What does this command do?

The --target ./package flag tells pip to install the library and all its dependencies into a local folder called package/ instead of into your virtual environment. This folder becomes part of your zip file that Lambda unpacks at runtime.

  • Confirm the package/ directory was created by running:
ls package/

You should see folders like anthropic, httpx, pydantic, and others. These are the SDK's dependencies.

  • Create the deployment zip file by running these commands:
cd package
zip -r ../deployment-package.zip .
cd ..
zip deployment-package.zip lambda_function.py

What do these commands do?

The first zip -r command adds all SDK files from inside package/ to the zip at the root level (Lambda expects dependencies at the root, not nested in a subfolder).

The second zip command adds your lambda_function.py to the same zip. Now the deployment package contains both your code and its dependencies.

  • Upload the deployment package to your Lambda function by running:
aws lambda update-function-code --function-name LeadQualificationFunction --zip-file fileb://deployment-package.zip
  • You should see a JSON response containing your function's details, including "LastUpdateStatus": "Successful" or "LastUpdateStatus": "InProgress".

Seeing "Function not found" or access denied?

Make sure the --function-name matches exactly what you named your Lambda in Step 1 (LeadQualificationFunction). Function names are case-sensitive.

If you see an access denied error, confirm your AWS CLI is configured with credentials that have permission to update Lambda functions.

Help me fix an error when uploading my Lambda deployment package.

Your Lambda function is deployed and ready to qualify leads. Next up, you'll connect it to API Gateway so it can receive live HTTP requests.

Deploy the API and Test End-to-End

Your Lambda function is packaged and ready to qualify leads. The missing piece is a way for the outside world to reach it.

In this step, you will create a public REST API endpoint using Amazon API Gateway. This gives your Lambda function a URL that any client can call with a simple HTTP request.

In this step, get ready to:
  • Create a REST API with a POST method integrated to your Lambda function.
  • Deploy the API and test it with sample lead inquiries using curl.
  • Verify qualified leads are stored in DynamoDB with AI-generated analysis.
Create a REST API in API Gateway

API Gateway acts as the front door for your Lambda function. You will create a REST API with a single resource path and connect it to your function using Lambda Proxy integration.

  • Open the AWS Console and navigate to the API Gateway service.
  • Click Create API.
  • Find the REST API card (not the one labelled "private") and click Build.
  • For API name, enter LeadQualificationAPI.
  • Keep API endpoint type set to Regional.
  • For IP address type, select IPv4.
  • Click Create API.

You now have an empty REST API. Next, create the resource path that clients will call.

  • Click Create resource.
  • Keep Proxy resource turned off.
  • For Resource name, enter qualify-lead.
  • Keep CORS turned off.
  • Click Create resource.

Now add a POST method to this resource and integrate it with your Lambda function.

  • Select the /qualify-lead resource, then click Create method.
  • For Method type, select POST.
  • For Integration type, select Lambda function.
  • Turn on Lambda proxy integration.
  • For Lambda function, select the region where you created your function, then enter LeadQualificationFunction.
  • Keep Default timeout turned on.
  • Click Create method.

What is Lambda proxy integration?

Lambda proxy integration passes the entire HTTP request (headers, query parameters, body) directly to your Lambda function as the event parameter. Your function is responsible for parsing the request and formatting the response.

This is why your lambda_handler function returns a dictionary with statusCode, headers, and body fields. API Gateway expects that exact format.

What about Lambda permissions?

When you create the method through the console, API Gateway automatically adds a resource-based policy that grants it permission to invoke your Lambda function. You may see a confirmation dialog asking you to allow this. Click OK to confirm.

Don't see your Lambda function in the dropdown?

Make sure you selected the same AWS region in the Lambda function field as the region where you created LeadQualificationFunction. The function must exist in the same region for the dropdown to list it.

help me find my Lambda function in API Gateway

Deploy the API and test with curl

Your API exists but is not yet accessible from the internet. Deploying it to a stage creates a public invoke URL you can call.

  • In the Resources pane, click Deploy API.
  • For Stage, select New stage.
  • For Stage name, enter prod.
  • Click Deploy.

You should now see your prod stage with an Invoke URL displayed under Stage details.

  • In the main navigation pane, click Stages.
  • Under Stage details, click the copy icon next to the Invoke URL.
  • Save this URL. You will append /qualify-lead to it when calling your API.

What does the invoke URL look like?

Your full endpoint will look like this: https://<api-id>.execute-api.<region>.amazonaws.com/prod/qualify-lead. The <api-id> and <region> parts are unique to your deployment.

Now test your API with a real lead inquiry. Open a terminal window on your computer.

  • Send your first test lead (a hot Botox inquiry) by running this command:
curl -X POST [[API_URL="your invoke URL ending in /prod/qualify-lead"]] \
  -H "Content-Type: application/json" \
  -d '{"inquiry": "I am interested in Botox for my forehead wrinkles, saw your Instagram ad", "source": "instagram"}'

What does this curl command do?

  • curl -X POST sends an HTTP POST request to your API endpoint.
  • -H "Content-Type: application/json" tells the API you are sending JSON data.
  • -d '{...}' provides the request body containing the lead inquiry text and source.

You should see a JSON response containing a lead_id and a qualification object with fields like lead_score, urgency, service_match, and follow_up_message.

  • Send a second test lead (a warm body contouring price inquiry) by running:
curl -X POST [[API_URL="your invoke URL ending in /prod/qualify-lead"]] \
  -H "Content-Type: application/json" \
  -d '{"inquiry": "Do you offer payment plans for body contouring? Checking prices at a few places.", "source": "website"}'

This second inquiry mentions price comparison and multiple options. You should see a lower lead score and likely a "warm" or "cold" urgency rating.

Seeing an Internal Server Error?

A 500 error usually means your Lambda function encountered a problem. Common causes:

  • The ANTHROPIC_API_KEY environment variable is not set correctly on the Lambda function. Double check it in the Lambda console under Configuration > Environment variables.
  • The DYNAMODB_TABLE_NAME environment variable does not match your actual table name (QualifiedLeads).
  • Your Lambda execution role does not have permission to write to DynamoDB. Check the role's attached policies.

my curl command returns a 500 Internal Server Error from my lead qualification API

Verify qualified leads in DynamoDB

Your API sent the lead data to Claude for analysis and stored the result in DynamoDB. You can verify this by checking the table directly.

  • Navigate to the DynamoDB service in the AWS Console.
  • Click Tables in the left navigation pane.
  • Click on your QualifiedLeads table.
  • Click Explore table items.

You should see the two test leads you just submitted. Each item contains the AI-generated qualification data.

  • Click on one of the items to expand it.
  • Review the fields: lead_score, urgency, service_match, buyer_signals, follow_up_message, reasoning, and timestamp.

What should I see?

The Botox inquiry should have a high lead score (likely 7-9) with an urgency of "hot" or "warm" and a service match of "Injectables". It mentions a specific service and references an ad, which are strong buyer signals.

The body contouring inquiry should have a lower score (likely 4-6) with "warm" or "cold" urgency. The price-comparison language and checking multiple places signal hesitation.

Table is empty?

If you see no items in the table, go back and check the curl response from the previous substep. If the curl returned a 200 status with a valid JSON body, the items should be in DynamoDB. If you received an error, fix the Lambda issue first.

Also confirm the table name matches exactly. Your Lambda function's DYNAMODB_TABLE_NAME environment variable must be QualifiedLeads (case-sensitive).

my DynamoDB QualifiedLeads table is empty after calling my lead qualification API

Secret mission

Build a Revenue Leak Report

Your lead qualification API handles one inquiry at a time. But what about the leads that slip through the cracks? In this secret mission, you will add a batch analysis endpoint that scans all recent leads, sends them to Claude for pattern recognition, and generates a Revenue Leak Report identifying which leads are going cold and how to recover them.

Clean Up Your Resources

Clean Up Your Resources

This project uses AWS free tier resources for Lambda, API Gateway, and DynamoDB. Lambda and API Gateway only charge per invocation, so there are no ongoing costs while the API is idle. The only pay-per-use cost is the Anthropic API, which is charged per call (approximately $0.01-0.05 total for testing).

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • DynamoDB table: QualifiedLeads.
  • Lambda function: LeadQualificationFunction.
  • API Gateway REST API: LeadQualificationAPI.
  • Local project folder with Python virtual environment and deployment package.
  • Anthropic API key (stored as Lambda environment variable).

Keep everything running

No action needed. Choose this if you're still actively building or want to keep the API live as a demo piece.

  • All three AWS resources (Lambda, API Gateway, DynamoDB) stay within free tier for light usage.
  • Your API endpoint remains live and callable at your API Gateway invoke URL.
  • You will only incur Anthropic API charges if you make additional calls to the endpoint.

Pause - I'll come back to this later

No immediate action is needed. Lambda and API Gateway only charge on invocation, so your resources are effectively paused while unused.

  • Your API will remain callable but cost nothing while idle.
  • DynamoDB stays within free tier (25 GB storage, 25 read/write capacity units) as long as you don't exceed those limits across your account.
  • Consider rotating your Anthropic API key in the Anthropic Console if you won't use it for an extended period.

Delete - I don't want to use this again

Remove all project resources and start fresh. Follow each step below to fully clean up.

Delete the API Gateway REST API
  • Open the APIs page of the API Gateway console.
  • Select LeadQualificationAPI.
  • Choose Actions, then Delete.
  • Choose Delete to confirm.
Delete the Lambda function
  • Open the Functions page of the Lambda console.
  • Select LeadQualificationFunction.
  • Choose Actions, then Delete.
  • Type confirm in the text input field and choose Delete.
Delete the DynamoDB table
  • Open the Tables page of the DynamoDB console.
  • Select QualifiedLeads.
  • Choose Delete.
  • Enter delete in the text box and choose Delete table.
Delete your local project files
  • Remove your project folder from the Desktop by running:
cd ~/Desktop
rm -rf lead-qualification-api

On Windows, use:

cd ~\Desktop
Remove-Item -Recurse -Force lead-qualification-api
Rotate or disable your Anthropic API key
  • Open the Anthropic Console at console.anthropic.com.
  • Navigate to the API Keys section.
  • Locate the key you used for this project and disable or delete it.
  • If you plan to reuse the Anthropic account, generate a fresh key for your next project instead.

Nice Work!

Nice Work!

Nice work! You just built and deployed an AI-powered lead qualification API that turns raw inquiry text into structured business intelligence in real time.

You've learned how to:

  • Built a Python Lambda function that integrates with Claude's Messages API to perform AI-powered lead qualification with structured JSON responses.
  • Designed a structured system prompt that extracts business intelligence (lead score, urgency, service match, follow-up message) from raw inquiry text.
  • Deployed a live REST API endpoint via API Gateway backed by Lambda and DynamoDB for real-time lead analysis and pipeline storage.
  • Secret Mission: Built a batch Revenue Leak Report endpoint that scans recent leads and identifies at-risk revenue with recommended recovery actions.

Ready to quiz yourself?