Build a Serverless Lead Intake API

Build a POST API that validates and stores lead data in DynamoDB.

Introduction

30 Second Summary

Every time someone fills out a "Contact Us" form on a business website, that data needs to go somewhere useful. Behind the scenes, an API endpoint catches that submission, checks if it looks valid, and stores it for the sales team to follow up.

In this project, you will build a serverless lead intake API that accepts form submissions, validates the data, and stores valid leads in a database.

What You'll Build

A REST API endpoint powered by AWS Lambda, Amazon API Gateway, and Amazon DynamoDB that processes lead submissions with input validation and proper error handling.

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

  • A DynamoDB table designed for lead storage with a composite key schema that supports tracking submissions over time.
  • A Python Lambda function that validates incoming data and returns proper HTTP status codes for success and error cases.
  • A deployed API Gateway endpoint with a POST method using Lambda proxy integration.
  • A Postman collection configured to test your API with valid and invalid payloads.
  • Secret Mission: Add a duplicate-detection check that prevents the same email from being submitted twice within 24 hours.

Are there any prerequisites?

You need an AWS account with IAM admin access and familiarity with the AWS Console. You should have a basic understanding of Lambda functions and API Gateway from prior projects.

Set Up Postman

Before you can test an API, you need a tool that can send requests and inspect responses. Postman is the industry-standard tool for API testing. It lets you craft requests, organize them into collections, and inspect every detail of the response.

In this step, you will install Postman, set up a workspace to keep your requests organized, and send a test request to confirm everything works.

In this step, get ready to:
  • Install Postman on your machine.
  • Create a workspace called "Lead Intake API".
  • Verify the setup with a test GET request.
Install Postman

Postman is a desktop application you download and install like any other app.

macOS

  • Open your browser and go to https://www.postman.com/downloads/.
  • Choose Mac Apple Chip if you have Apple Silicon, or Mac Intel if you have an older Mac.
  • Once the download finishes, unzip the file in your Downloads folder.
  • Drag the Postman app into your Applications folder.

Prefer Homebrew?

If you have Homebrew installed, you can install Postman by running this command in Terminal instead:

brew install --cask postman

Windows

  • Open your browser and go to https://www.postman.com/downloads/.
  • Download the Windows 64-bit installer.
  • Locate the downloaded .exe file and double-click it to run the installer.
  • Follow the on-screen prompts to complete the installation.

Download not starting?

Make sure you selected the correct version for your operating system. The Postman downloads page auto-detects your OS, but you can manually choose if needed.

help me troubleshoot my Postman installation

Create your workspace

Workspaces in Postman organize your requests into logical groups. You will create one workspace for all the requests in this project.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Postman and press Enter to open it.

Create a free account?

Postman will prompt you to sign in or create an account. Creating a free account is optional but recommended. It lets you access your API requests from different machines and save your work across sessions.

If you prefer to skip the account, look for a link to continue without signing in.

  • In the top left of Postman, click Workspaces.
  • Click Create Workspace.
  • Enter Lead Intake API as the workspace name.
  • Select Personal for the visibility type.
  • Click Create.

Postman opens your new workspace. This is where all your API test requests will live for this project.

Verify Postman with a test request

To confirm Postman is working correctly, you will send a simple GET request to a public test endpoint. This endpoint always returns a 200 response, so if you see that status code, everything is set up correctly.

  • In your Lead Intake API workspace, click the + button to open a new request tab.
  • Make sure the method dropdown is set to GET.
  • In the URL field, enter https://httpbin.org/get.
  • Click Send.

✔️ I see 200 OK

Your response panel shows a 200 OK status and a JSON body with details about your request. Postman is working correctly.

ⓧ I see an error or no response

  • Check that the URL is exactly https://httpbin.org/get with no extra spaces or typos.
  • Make sure your internet connection is active.
  • If you see a certificate error, check that your system clock is set to the correct time.

help me fix my Postman test request

What is httpbin.org?

httpbin.org is a free public service that echoes back whatever request you send it. Developers use it to test HTTP clients without needing a real backend. The /get endpoint simply returns your request headers and parameters as JSON.

Your testing tool is ready. Next up, you will create the DynamoDB table that stores the leads your API receives.

Create the DynamoDB Table

Postman is ready to go. Now you need a place to store the lead data that your API will receive.

In this step, you will create a DynamoDB table designed specifically for lead storage. The table's key structure determines how you can query your data later, so getting the schema right from the start is important.

In this step, get ready to:
  • Create a DynamoDB table with a composite primary key.
  • Understand why this schema design supports real lead tracking patterns.
Navigate to DynamoDB and create the Leads table

Your Lambda function will write lead data into this table. You need to create it first so the function has somewhere to store records.

  • In the AWS Management Console, type DynamoDB into the search bar at the top and select DynamoDB from the results.
  • In the left navigation pane, click Tables.
  • Click Create table.
  • For Table name, enter Leads.
  • For Partition key, enter email and keep the type as String.
  • For Sort key, enter submitted_at and keep the type as String.
  • Under Table settings, select Customize settings.
  • Under Read/write capacity settings, select On-demand as the capacity mode.
  • Leave all other settings at their defaults and click Create table.

Wait for the table status to change from Creating to Active. This usually takes a few seconds.

Why on-demand capacity mode?

On-demand mode means DynamoDB automatically scales to handle whatever traffic your table receives. You do not need to estimate read/write capacity upfront.

For a learning project with unpredictable traffic, this is simpler than provisioned mode where you would set fixed capacity units.

Table stuck in Creating status?

Table creation typically completes within 5-10 seconds. If it stays in Creating for longer than a minute, try refreshing the page. If the table does not appear at all, check that you have the correct IAM permissions for DynamoDB.

Help me troubleshoot my DynamoDB table creation

Understand the schema design

The key choices you just made are not arbitrary. They determine how you can query and organize your lead data.

Why email as partition key and timestamp as sort key?

Using email as the partition key means DynamoDB groups all submissions from the same person together. Using submitted_at as the sort key means those submissions are ordered chronologically within each group.

This mirrors how real CRM systems track lead engagement. A single person might fill out a contact form multiple times over weeks or months. Your schema stores each submission as a separate record while keeping them queryable by email address.

This design also enables the Secret Mission later, where you will query for recent submissions from the same email to detect duplicates.

What is a composite primary key?

A composite primary key combines a partition key and a sort key. Together, they uniquely identify each item in the table. The same email can appear multiple times as long as the submitted_at timestamp is different.

Your database is ready to receive lead records. Next up, you will write the Lambda function that validates incoming data and stores it in this table.

Write the Lambda Function

Your DynamoDB table is ready to store leads. Now you need the brains of the operation: a Lambda function that receives incoming requests, checks that the data is valid, and writes good leads to the table.

This function acts as the gatekeeper between the outside world and your database. It will reject bad data with clear error messages and only allow properly formatted leads through.

In this step, get ready to:
  • Create a Lambda function with the Python 3.13 runtime.
  • Write validation logic that checks incoming lead data.
  • Test the function with a sample API Gateway event.
Create the Lambda function

You need a Lambda function that will run your Python code every time a request hits the API endpoint. You will also give it permission to write to your Leads table.

  • In the AWS Console, type Lambda in the top search bar and select Lambda from the results.
  • Click Create function.
  • Leave Author from scratch selected.
  • For Function name, enter LeadIntakeFunction.
  • For Runtime, select Python 3.13.
  • Expand Change default execution role under Permissions.
  • Leave Create a new role with basic Lambda permissions selected. You will add DynamoDB access to this role in a moment.
  • Click Create function.

Lambda creates your function with a default code file called lambda_function.py.

Why Python 3.13?

Python 3.13 runs on Amazon Linux 2023 and is supported until June 2029. The Lambda Python runtimes include boto3 (the AWS SDK for Python) pre-installed, so you do not need to upload any extra dependencies.

Now grant the function permission to write to DynamoDB.

  • 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.
  • Click Add permissions, then Attach policies.
  • Search for AmazonDynamoDBFullAccess and check the box next to it.
  • Click Add permissions.

You should see AmazonDynamoDBFullAccess listed under Permissions policies for the role.

Why AmazonDynamoDBFullAccess?

For a learning project, the AmazonDynamoDBFullAccess managed policy is quick and simple. In production, you would create a custom policy granting only dynamodb:PutItem on your specific table for tighter security.

Don't see the policy?

Make sure you are searching in the IAM console (the tab that opened from the role link), not the Lambda console. The search field is case-sensitive for the policy name.

Help me verify my DynamoDB policy is attached correctly

Write the handler code

Your handler will parse the incoming JSON body, validate that all required fields are present, and write valid leads to DynamoDB. If anything is missing, it returns a 400 error with a clear message.

  • Switch back to your Lambda function tab in the browser.
  • Click the Code tab.
  • In the Code source editor, select all the default code in lambda_function.py and delete it.
  • Replace it with the following code:
import json
import boto3
from datetime import datetime, timezone

# Connect to the Leads table in DynamoDB
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Leads")


def lambda_handler(event, context):
    # Parse the JSON body from the incoming request
    try:
        body = json.loads(event.get("body") or "{}")
    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Invalid JSON in request body"}),
        }

    # Check that all required fields are present
    errors = []
    required_fields = ["name", "email", "phone", "service_interest"]

    for field in required_fields:
        if not body.get(field):
            errors.append(f"Missing required field: {field}")

    if errors:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"errors": errors}),
        }

    # Prepare the lead data with a UTC timestamp
    email = body["email"].strip().lower()
    submitted_at = datetime.now(timezone.utc).isoformat()

    item = {
        "email": email,
        "submitted_at": submitted_at,
        "name": body["name"].strip(),
        "phone": body["phone"].strip(),
        "service_interest": body["service_interest"].strip(),
    }

    # Write the lead to DynamoDB
    try:
        table.put_item(Item=item)
    except Exception as e:
        return {
            "statusCode": 500,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Failed to save lead", "detail": str(e)}),
        }

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"message": "Lead received successfully", "lead_id": f"{email}#{submitted_at}"}),
    }

What does this code do?

  • json.loads(event.get("body") or "{}") parses the request body that API Gateway sends. If the body is missing or empty, it defaults to an empty dictionary.
  • The validation loop checks four required fields: name, email, phone, and service_interest. If any are missing, it collects all errors and returns them at once so the client knows exactly what to fix.
  • The email is lowercased and stripped to ensure consistent storage. The submitted_at timestamp in UTC becomes the sort key in DynamoDB.
  • table.put_item(Item=item) writes the lead to your Leads table. If DynamoDB fails, the function returns a 500 error with the failure detail.
  • The response format (statusCode, headers, body) is required by API Gateway Lambda proxy integration. The body must be a JSON string, not a dictionary.
  • Click Deploy to save your code changes to Lambda.

You should see a green banner confirming the function was updated successfully.

Seeing a deployment error?

Make sure you deleted all of the default code before pasting. Leftover lines from the default handler can cause syntax errors.

Also check that you have not accidentally introduced extra indentation. Python is whitespace-sensitive.

Help me troubleshoot my Lambda deployment error

✔️ Awesome, I've got everything!

Great. Make sure you clicked Deploy and saw the green success banner before moving on.

ⓧ I'd like to double check the full code

Here is the complete lambda_function.py file. Compare it with what you have in the Lambda code editor.

import json
import boto3
from datetime import datetime, timezone

dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Leads")


def lambda_handler(event, context):
    try:
        body = json.loads(event.get("body") or "{}")
    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Invalid JSON in request body"}),
        }

    errors = []
    required_fields = ["name", "email", "phone", "service_interest"]

    for field in required_fields:
        if not body.get(field):
            errors.append(f"Missing required field: {field}")

    if errors:
        return {
            "statusCode": 400,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"errors": errors}),
        }

    email = body["email"].strip().lower()
    submitted_at = datetime.now(timezone.utc).isoformat()

    item = {
        "email": email,
        "submitted_at": submitted_at,
        "name": body["name"].strip(),
        "phone": body["phone"].strip(),
        "service_interest": body["service_interest"].strip(),
    }

    try:
        table.put_item(Item=item)
    except Exception as e:
        return {
            "statusCode": 500,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"error": "Failed to save lead", "detail": str(e)}),
        }

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"message": "Lead received successfully", "lead_id": f"{email}#{submitted_at}"}),
    }
Test the function with a sample event

Before connecting API Gateway, you should confirm the function works on its own. The Lambda console lets you send a test event that mimics what API Gateway would send.

  • Click the Test tab on your function's page.
  • Under Test event, choose Create new event.
  • For Event name, enter ValidLeadTest.
  • Replace the default JSON with the following test event:
{
  "body": "{\"name\": \"Jane Smith\", \"email\": \"jane@example.com\", \"phone\": \"555-0123\", \"service_interest\": \"Web Development\"}",
  "httpMethod": "POST",
  "path": "/leads"
}

Why is the body a string inside a string?

API Gateway Lambda proxy integration wraps the request body as a JSON-encoded string inside the event object. Your code then parses that string with json.loads(). This test event mimics that exact structure so you get a realistic test.

  • Click Save.
  • Click Test.

Under Execution result, expand Details. You should see a response with statusCode of 200 and a body containing Lead received successfully.

Seeing a 500 error about failed to save lead?

This usually means the function does not have permission to write to DynamoDB. Go back to the Configuration tab, click Permissions, and confirm that AmazonDynamoDBFullAccess is attached to the role.

Also verify that your DynamoDB table is named exactly Leads (capital L) and is in the same AWS region as your Lambda function.

Help me fix my Lambda 500 error

  • Open the DynamoDB console in a new tab to confirm the lead was stored.
  • Click Tables, then click Leads.
  • Click Explore table items.
  • You should see one item with the email jane@example.com and a submitted_at timestamp.

Your Lambda function is tested and working. Next up, you will connect it to an API Gateway endpoint so it can receive requests from the outside world.

Create the API Gateway Endpoint

Your Lambda function can validate and store leads, but it has no way for the outside world to reach it. API Gateway solves this by giving your function a public URL that accepts HTTP requests.

In this step, you will create a REST API with a POST endpoint, wire it to your Lambda function, deploy it, and confirm end-to-end connectivity from your terminal.

In this step, get ready to:
  • Create a REST API with a /leads resource and POST method.
  • Deploy the API to a stage and copy the invoke URL.
  • Test end-to-end connectivity with a curl command.
Create the REST API and configure the POST method

A REST API in API Gateway acts as the front door for your backend. You will create an API, add a /leads resource, and attach a POST method that forwards requests straight to your Lambda function.

  • In the AWS Console, use the search bar at the top to search for API Gateway and open it.
  • Click Create API.
  • Find the REST API card (not the Private one) and click Build.
  • For API name, enter LeadIntakeAPI.
  • Keep API endpoint type set to Regional.
  • Click Create API.

You should now see the Resources pane showing the root resource /.

  • Select the root resource /, then click Create resource.
  • Keep Proxy resource turned off.
  • For Resource name, enter leads.
  • Keep CORS (Cross Origin Resource Sharing) turned off.
  • Click Create resource.

The /leads resource now appears in the tree.

  • Select the /leads resource, then click Create method.
  • For Method type, select POST.
  • For Integration type, select Lambda function.
  • Turn on Lambda proxy integration.
  • For Lambda function, enter LeadIntakeFunction and select it from the dropdown.
  • Click Create method.

What is Lambda proxy integration?

With Lambda proxy integration, API Gateway passes the entire incoming HTTP request (headers, body, query parameters) directly to your Lambda function as the event object.

Your function then controls the full response, including the status code and headers. This is simpler than non-proxy integration because you do not need to configure mapping templates in API Gateway.

Don't see your Lambda function in the dropdown?

Make sure your Lambda function is in the same AWS Region as your API. Check the region selector in the top-right corner of the console.

Also confirm the function name is exactly LeadIntakeFunction with no extra spaces.

help me find my Lambda function in API Gateway

Deploy the API and copy the invoke URL

An API in API Gateway is not publicly accessible until you deploy it to a stage. A stage is like a label (dev, prod) that gives your API a live URL.

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

You should land on the Stages page showing your dev stage.

  • Under Stage details, find the Invoke URL and click the copy icon next to it.
  • Paste the URL somewhere you can access it (a text file or sticky note works fine).

What does the invoke URL look like?

The URL follows this pattern:

https://{api-id}.execute-api.{region}.amazonaws.com/dev

To hit your POST endpoint, you will append /leads to this base URL.

Test end-to-end connectivity with curl

Before moving to Postman, a quick curl command from your terminal confirms the entire chain (API Gateway to Lambda to DynamoDB) is working.

  • Open Terminal by pressing Cmd+Space (macOS) or the Windows key (Windows), typing Terminal, and pressing Enter.
  • Send a test POST request by running this command:
curl -X POST [[INVOKE_URL="your invoke URL"]]/leads \
  -H "Content-Type: application/json" \
  -d '{"name": "Test User", "email": "test@example.com", "phone": "555-0100", "service_interest": "Web Design"}'

What does this command do?

  • -X POST sets the HTTP method to POST.
  • -H "Content-Type: application/json" tells the API the body is JSON.
  • -d '{...}' sends the JSON body containing lead data.

You should see a JSON response containing "message": "Lead received successfully" and a lead_id value.

Seeing an error response?

If you get a 403 Forbidden or Missing Authentication Token message, double-check that you appended /leads to the invoke URL. Hitting the base URL without a resource path causes this error.

If you see a 500 error, return to the Lambda console and check the function's CloudWatch Logs for details. The most common cause is the IAM role missing DynamoDB permissions.

help me debug my API Gateway curl request

Your API is live and accepting requests. Next up, you will use Postman to build a proper test collection that covers both valid and invalid submissions.

Test with Postman

Your API endpoint is deployed and reachable from anywhere on the internet. The last step is proving it behaves correctly under different conditions.

Professional developers never test just the happy path. They deliberately send bad data to confirm their API rejects it with clear error messages. In this step, you will use Postman to send three requests: one valid submission, one missing a required field, and one with an empty body.

In this step, get ready to:
  • Create a Postman collection for your API tests.
  • Send a valid lead and confirm a 200 response.
  • Send invalid payloads and confirm 400 responses, then verify DynamoDB only stored the valid lead.
Create a collection and submit a valid lead

Collections in Postman group related requests together. You will create one collection to hold all your test requests for the Lead Intake API.

  • Switch back to Postman in your Lead Intake API workspace.
  • In the left sidebar, click the + button and select Collection.
  • Name the new collection Lead Intake API Tests and press Enter.

Now add your first request to the collection.

  • Hover over the Lead Intake API Tests collection in the sidebar and click Add request.
  • Name the request Submit Valid Lead.
  • Change the method dropdown from GET to POST.
  • Paste your API invoke URL into the URL field. The full URL looks like this:
[[API_INVOKE_URL="https://your-api-id.execute-api.your-region.amazonaws.com/dev/leads"]]

Where is my invoke URL?

You copied this URL at the end of Step 4 when you deployed the API to the dev stage. It ends with /dev. Make sure you append /leads to the end so the full path is /dev/leads.

  • Click the Body tab below the URL field.
  • Select raw.
  • In the format dropdown (which defaults to Text), select JSON.
  • Paste the following JSON into the body editor:
{
    "name": "Jane Smith",
    "email": "jane.smith@example.com",
    "phone": "555-123-4567",
    "service_interest": "Web Development"
}

What do these fields represent?

These are the four required fields your Lambda function validates. Each one represents a piece of data a potential client would fill out on a lead capture form.

  • name and email identify the person.
  • phone gives a contact number.
  • service_interest tells the business what service the lead cares about.
  • Click Send.

In the response pane below, you should see a 200 OK status and a JSON body containing a success message and a lead_id.

Seeing a 403 or 500 error?

  • Double-check that your URL includes /leads at the end. A missing resource path causes a 403.
  • Confirm the method is set to POST. A GET request to this endpoint returns a 403 because no GET method is configured.
  • If you see a 500, check that your Lambda function's IAM role has the AmazonDynamoDBFullAccess policy attached.

Why is my POST request returning an error?

Test with a missing required field

A good API rejects incomplete data and tells the caller exactly what is missing. Your second request proves this by omitting the email field.

  • Hover over the Lead Intake API Tests collection in the sidebar and click Add request.
  • Name the request Submit Invalid Lead - Missing Email.
  • Set the method to POST.
  • Paste the same invoke URL you used for the first request.
  • Click the Body tab, select raw, and choose JSON from the format dropdown.
  • Paste this JSON, which deliberately leaves out email:
{
    "name": "John Doe",
    "phone": "555-987-6543",
    "service_interest": "SEO Audit"
}
  • Click Send.

You should see a 400 Bad Request status. The response body contains an errors array with the message Missing required field: email.

Why 400 and not 500?

A 400 status means the client sent bad data. A 500 means the server itself broke. By returning 400 for missing fields, your API clearly communicates that the problem is with the request, not the server.

Test with an empty body and verify DynamoDB

The final test sends a request with no body at all. After running all three tests, you will check DynamoDB to confirm only the valid lead was actually stored.

  • Hover over the Lead Intake API Tests collection and click Add request.
  • Name the request Submit Invalid Lead - Empty Body.
  • Set the method to POST.
  • Paste the same invoke URL.
  • Leave the Body tab set to none (the default for a new request).
  • Click Send.

You should see a 400 Bad Request status. The response body lists all four missing fields in the errors array.

What should I see in the response?

The errors array contains four messages: one for each required field (name, email, phone, service_interest). This tells the caller exactly which fields to fix.

Now confirm that only the valid lead made it into your database.

  • Return to the AWS Console and navigate to the DynamoDB service.
  • Click Tables in the left sidebar, then click your Leads table.
  • Click the Explore table items button.
  • Click Run to scan the table.

You should see exactly one item in the results: the valid lead from Jane Smith. The two invalid requests were correctly rejected and never reached DynamoDB.

Seeing zero items or unexpected data?

  • If you see zero items, go back to Postman and re-send the Submit Valid Lead request. Check that the response status is 200.
  • If you see more than one item, you may have sent the valid request multiple times. Each submission creates a new entry because the submitted_at timestamp differs each time.

Why doesn't my DynamoDB table show the expected items?

Secret mission

Add Duplicate Detection

Your API currently accepts the same lead over and over. In real lead intake systems, duplicate submissions waste sales team time and skew analytics. Can you add logic that rejects submissions from the same email within a 24-hour window?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. All three AWS services used in this project (DynamoDB, Lambda, API Gateway) are serverless and only charge per request, so there are no ongoing costs while they sit idle within free tier limits.

Resources you used:

  • DynamoDB table Leads
  • Lambda function LeadIntakeFunction
  • API Gateway REST API LeadIntakeAPI (deployed to dev stage)
  • Postman workspace Lead Intake API

Keep everything running

No action needed. Choose this if you plan to keep testing your API or building on top of it.

  • DynamoDB free tier covers 25 GB of storage and 25 provisioned Write/Read Capacity Units (always free).
  • Lambda free tier covers 1 million requests per month (always free).
  • API Gateway free tier covers 1 million REST API calls per month for your first 12 months on AWS.
  • Postman remains installed on your machine. Your workspace and collections are saved to your Postman account.

Pause - I'll come back to this later

All three AWS services are serverless and do not run continuously. There are no processes to stop or pause. Your resources will sit idle at no cost within free tier limits.

  • You can close Postman. Your workspace and collections will be available when you reopen it.
  • You can sign out of the AWS Console. Your resources will remain available when you sign back in.

Delete - I don't want to use this again

Remove all project resources and start fresh. Follow these steps to delete each resource.

Delete the DynamoDB table:

  • Open the DynamoDB console.
  • In the left navigation pane, choose Tables.
  • Select the checkbox beside the Leads table.
  • Choose Delete.
  • Confirm your choice when prompted, then choose Delete table.

Delete the Lambda function:

  • Open the Functions page of the Lambda console.
  • Select LeadIntakeFunction.
  • Choose Actions, then Delete.
  • Type confirm in the text input field and choose Delete.

Delete the API Gateway REST API:

  • Open the APIs page of the API Gateway console.
  • Select LeadIntakeAPI.
  • Choose Actions, then Delete.
  • Choose Delete to confirm.

Delete the Postman workspace (optional):

  • In Postman, right-click the Lead Intake API workspace in the sidebar.
  • Choose Delete Workspace and confirm.
  • To uninstall Postman entirely, drag it from your Applications folder to the Trash (macOS) or use Settings > Apps > Installed apps to uninstall (Windows).

Nice Work!

Nice Work!

Nice work! You just built a serverless lead intake API that validates incoming data, stores it in DynamoDB, and returns proper HTTP status codes.

You've learned how to:

  • Built a DynamoDB table with a composite key schema (partition key + sort key) designed for real lead tracking and time-based queries.
  • Wrote a Python Lambda function with input validation and proper error handling that returns 200, 400, and 500 status codes.
  • Created and deployed a REST API with a POST endpoint using Lambda proxy integration in API Gateway.
  • Configured and used Postman to test API endpoints with valid and invalid payloads across a professional test collection.
  • Secret Mission: Implemented duplicate-detection logic with time-based DynamoDB queries and 409 Conflict responses.

Ready to quiz yourself?