Deploy an AI Chatbot as a Serverless API
Deploy a Python AI chatbot to AWS Lambda and expose it via API Gateway.
Introduction
30 Second Summary
Building an AI chatbot on your laptop is great for testing, but nobody else can use it. To share your AI with the world, you need to deploy it somewhere anyone can reach.
In this project, you will take Camay, your AI rehabilitation chatbot, and deploy her as a live API on AWS using serverless infrastructure. Anyone with the right API key can send her a message and get back personalized rehabilitation guidance.
What You'll Build
You send a POST request to your public URL with a message, and seconds later Camay responds with personalized stroke rehabilitation guidance powered by Google Gemini. No servers to manage, no cost within the free tier.
By the end of this project, you'll have:
- A live HTTPS endpoint that anyone can call to chat with Camay and get back her AI rehabilitation response as JSON.
- A secured API that rejects unauthorized requests with a 403 response and only serves Camay's guidance to clients with the right key.
- A repeatable deployment workflow you can use for any Python-based AI function: package dependencies, upload to Lambda, expose via API Gateway, and test from the command line.
- Secret Mission: Add conversation memory with DynamoDB so Camay remembers past messages across requests.
Are there any prerequisites?
You'll need an AWS account, a Google Gemini API key, and Python installed on your machine. You should also be comfortable writing basic Python and have some experience calling LLM APIs.
Before We Start
Before We Start...
Before we dive in, let's lock in what you're about to build and why it matters. Deploying an AI chatbot to AWS serverless infrastructure is one of the most in-demand skills on AI Engineer job postings today.
Set Up Your AWS CLI and IAM User
Your Camay chatbot works locally, but to deploy it on AWS, you need a way to talk to AWS services directly from your terminal.
The AWS CLI is a command-line tool that lets you create, configure, and manage AWS services without clicking through the console. In this step, you'll install it, create an IAM user with the right permissions, and prove your terminal can reach your AWS account.
In this step, get ready to:
- Install AWS CLI v2 on your machine.
- Create an IAM user with programmatic access credentials.
- Verify your terminal can communicate with your AWS account.
Install AWS CLI v2
The AWS CLI bridges your local terminal to AWS cloud services. You need version 2 (the current major release) installed before you can deploy any code.
- Check if the AWS CLI is already installed by running this command in your terminal:
aws --version
✔️ I see aws-cli/2.x.x
Your output starts with aws-cli/2. followed by a version number. You already have AWS CLI v2 installed and ready to go.
Skip ahead to the next substep.
ⓧ I see aws-cli/1.x.x
You have the older version installed. AWS CLI v1 is end-of-support, and this project requires v2. You need to upgrade.
- Open Programs and Features from your Control Panel.
- Select AWS Command Line Interface and click Uninstall.
- Download the AWS CLI v2 installer from this link:
https://awscli.amazonaws.com/AWSCLIV2.msi
- Run the downloaded AWSCLIV2.msi file and follow the on-screen prompts to install.
- Close all open terminal windows, then open a fresh Command Prompt.
- Run aws --version again. You should now see aws-cli/2. at the start of the output.
ⓧ Command not found
The AWS CLI is not installed on your machine yet. Let's install it now.
- Download the AWS CLI v2 installer from this link:
https://awscli.amazonaws.com/AWSCLIV2.msi
- Run the downloaded AWSCLIV2.msi file and follow the on-screen prompts to install.
- Close all open terminal windows, then open a fresh Command Prompt.
- Confirm the installation by running:
aws --version
You should see output starting with aws-cli/2. followed by version details.
Still seeing command not found after installing?
Make sure you closed ALL open terminal windows and opened a brand new one. The installer updates your system PATH, but only new terminals pick up PATH changes.
If that still doesn't work, the installer may not have updated your PATH automatically. The default install location on Windows is C:\Program Files\Amazon\AWSCLIV2\. Add that folder to your PATH environment variable manually, then restart your terminal.
Why isn't my aws command found after installing the AWS CLI on Windows?
Create an IAM user and configure credentials
IAM (Identity and Access Management) controls who can do what in your AWS account. You need an IAM user with programmatic access so the CLI can authenticate your commands on your behalf.
- Open your browser and sign in to the AWS Management Console at https://console.aws.amazon.com.
- Type IAM in the search bar at the top and select IAM from the results.
- Click Users in the left navigation pane.
- Click Create user.
- Enter camay-deployer as the user name.
- Click Next.
You should now see the permissions page where you choose how to grant this user access to AWS services.
- Select Attach policies directly.
- Type AdministratorAccess in the search box.
- Check the checkbox next to the AdministratorAccess policy.
- Click Next.
- Click Create user.
Why AdministratorAccess?
For learning purposes, this broad policy lets you create Lambda functions, API Gateways, and IAM roles without running into permission errors. In a production environment, you would follow the principle of least privilege and create a custom policy with only the specific permissions needed.
You should see a success banner confirming your user was created. Now you need to generate access keys so the CLI can authenticate as this user.
- Click on the user name camay-deployer to open their details page.
- Select the Security credentials tab.
- Scroll down to the Access keys section and click Create access key.
- Select Command Line Interface (CLI) as the use case.
- Check the confirmation checkbox at the bottom.
- Click Next.
- Click Create access key.
Save your keys now
You now see your Access Key ID and Secret Access Key. Copy both values and save them somewhere safe (a temporary text file or password manager). You will not be able to see the Secret Access Key again after leaving this page.
Click Download .csv file as a backup, then click Done.
Now connect those credentials to your terminal. The aws configure command will prompt you for four values one at a time.
- Run the configure command in your terminal:
aws configure
What will aws configure ask me?
The command prompts you four times. Enter these values at each prompt:
- AWS Access Key ID: paste the Access Key ID you just copied.
- AWS Secret Access Key: paste the Secret Access Key you just copied.
- Default region name: type us-east-1.
- Default output format: type json.
After you enter the fourth value, the command finishes silently. That means it worked.
Verify the connection
You've installed the CLI and configured credentials. Time to prove the connection actually works.
Before you run the next command, take a moment to predict: what information do you think AWS will send back to confirm your identity?
- Verify your connection to AWS by running:
aws sts get-caller-identity
You should see JSON output containing three fields: your Account ID, your UserId, and an Arn that ends with the user name you created.
What does this output mean?
The aws sts get-caller-identity command asks AWS to confirm who is calling. If your credentials are configured correctly, AWS responds with your account information.
- Account: your 12-digit AWS account number.
- UserId: a unique identifier for the IAM user.
- Arn: the full Amazon Resource Name, ending in user/camay-deployer.
Seeing an error instead of JSON?
If you get a message about invalid credentials or unable to locate credentials, double check that you pasted the correct Access Key ID and Secret Access Key during aws configure. You can re-run aws configure to overwrite the values.
If you see a network or timeout error, make sure you have an active internet connection and try again.
Help me fix my aws sts get-caller-identity error.
Your terminal can now reach AWS and authenticate as your IAM user. Next up, you'll write Camay's Lambda function and test it locally before deploying it to the cloud.
Build and Test Camay's Lambda Function Locally
Your AWS CLI is connected and ready to deploy. Before touching any cloud services, you need working chatbot code on your machine.
Writing the Lambda handler locally lets you verify Camay's logic works before you deploy it. This is your "baby version" that proves the AI responds.
In this step, get ready to:
- Create a project folder and install the Gemini SDK.
- Write a Lambda handler that calls Google Gemini with Camay's rehabilitation system prompt.
- Run the function locally and see Camay's AI response in your terminal.
Create the project folder and install the Gemini SDK
You need a dedicated folder for the Lambda function code and a local copy of the Google GenAI SDK so Python can talk to the Gemini API.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir camay-lambda
cd camay-lambda
- Confirm the folder exists by running:
ls
You should see the empty camay-lambda directory listed on your Desktop.
- Install the Google GenAI package by running:
pip install google-genai==2.14.0
You should see pip downloading and installing several packages, ending with a "Successfully installed" message.
Seeing a permission error or pip not found?
If pip is not recognized, try pip3 or python -m pip install google-genai==2.14.0 instead.
If you see a permissions error, add --user at the end of the install command.
Help me troubleshoot my pip install error.
Write the Lambda function setup and system prompt
Your Lambda function needs to import the Gemini SDK, read your API key from an environment variable, and define Camay's personality through a system prompt.
- Set your Gemini API key as an environment variable for this terminal session by running:
$env:GEMINI_API_KEY = "your-api-key-here"
Why an environment variable instead of hardcoding the key?
Lambda functions read secrets from environment variables at runtime. By setting the variable locally now, your code runs identically on your machine and in the cloud.
Replace your-api-key-here with your actual Gemini API key. Keep the quotes around it.
- Create lambda_function.py in your camay-lambda folder and add the imports and system prompt by pasting this code:
import json
import os
from google import genai
from google.genai import types
# Read the API key from the environment variable
GEMINI_API_KEY = os.environ["GEMINI_API_KEY"]
# Initialize the Gemini client
client = genai.Client(api_key=GEMINI_API_KEY)
# Define Camay's personality and expertise
SYSTEM_PROMPT = """You are Camay, a compassionate and knowledgeable AI rehabilitation assistant
for stroke survivors. You help users with:
- Setting and refining SMART rehabilitation goals
- Providing evidence-based exercise and wellness guidance
- Offering emotional support and encouragement during recovery
- Assessing risk levels and recommending professional consultation when needed
Always be empathetic, patient, and encouraging. If a user describes symptoms that suggest
a medical emergency, advise them to contact emergency services immediately.
Keep responses concise but warm."""
What does this code do?
os.environ["GEMINI_API_KEY"] reads your API key from the environment. If the variable is missing, Python raises a KeyError immediately so you know something is wrong.
genai.Client creates the connection to Google's Gemini API. The SYSTEM_PROMPT tells Gemini how Camay should behave in every conversation.
- Save lambda_function.py and verify your imports work by running:
python lambda_function.py
Your terminal should return to the prompt with no output and no error. The clean exit confirms your imports loaded and the API key was read successfully.
Seeing KeyError: 'GEMINI_API_KEY'?
This means Python could not find the environment variable. Make sure you set it in the same terminal session where you are running the script.
Run echo $env:GEMINI_API_KEY to confirm it prints your key. If it shows nothing, re-run the $env:GEMINI_API_KEY command from the previous substep.
Help me fix my GEMINI_API_KEY environment variable error.
Write the request handler and test locally
The handler function is what AWS Lambda calls every time a request arrives. It parses the incoming message, builds a conversation, and asks Gemini for a response.
- In lambda_function.py, add the handler function below the SYSTEM_PROMPT definition. This first half parses the request and builds the conversation:
def lambda_handler(event, context):
# Parse the incoming request body
try:
body = json.loads(event.get("body", "{}"))
except (json.JSONDecodeError, TypeError):
body = event
message = body.get("message", "")
history = body.get("history", [])
# Return an error if no message was provided
if not message:
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "message field is required"}),
}
# Build conversation contents from history
contents = []
for msg in history:
role = msg.get("role", "user")
contents.append(types.Content(role=role, parts=[types.Part.from_text(text=msg.get("content", ""))]))
# Add the current user message
contents.append(types.Content(role="user", parts=[types.Part.from_text(text=message)]))
What does this code do?
lambda_handler(event, context) is the function Lambda invokes on each request. The event parameter contains the HTTP request data.
The try/except block handles both API Gateway events (JSON string in body) and direct test invocations where the event IS the body. The contents list rebuilds the conversation history so Gemini has full context.
- Still in lambda_function.py, add the Gemini API call, return statement, and test block directly below the previous code (still inside the same function for the first part, then the test block at module level):
# Call the Gemini API with Camay's system prompt
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=contents,
config=types.GenerateContentConfig(
system_instruction=SYSTEM_PROMPT,
max_output_tokens=1024,
temperature=0.7,
),
)
assistant_message = response.text
# Return Camay's response as JSON
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"response": assistant_message,
"model": "gemini-2.5-flash",
}),
}
# Test block: only runs when executing the file directly
if __name__ == "__main__":
test_event = {
"body": json.dumps({
"message": "I had a stroke last month and I feel overwhelmed. Where do I start with recovery?"
})
}
result = lambda_handler(test_event, None)
print(json.dumps(json.loads(result["body"]), indent=2))
What does this code do?
client.models.generate_content sends the conversation to Gemini 2.5 Flash with Camay's system instruction, a token limit of 1024, and a temperature of 0.7 for warm but focused responses.
The if __name__ == "__main__" block only runs when you execute the file directly with python lambda_function.py. It simulates a Lambda event so you can test locally without deploying.
- Save lambda_function.py.
Before you run this, what do you think will happen? Will Camay respond with generic AI text, or something specific to stroke rehabilitation?
- Test Camay by running:
python lambda_function.py
You should see Camay's JSON response printed with rehabilitation-focused guidance. The output includes a response field with empathetic advice and a model field showing gemini-2.5-flash.
Seeing an API error or empty response?
If you see a 400 or 403 error from the Gemini API, double-check that your API key is valid and has the Generative Language API enabled in your Google Cloud project.
If the script hangs for more than 30 seconds, your network may be blocking the request. Try from a different network or check if a VPN is interfering.
Help me debug my Gemini API error.
- Create requirements.txt in the same camay-lambda folder with the project's sole dependency by running:
echo "google-genai==2.14.0" > requirements.txt
- Confirm both files exist by running:
ls
You should see lambda_function.py and requirements.txt listed in the output.
Why a requirements.txt if we already installed the package?
The local install let you test right away. The requirements.txt file tells the deployment process (next step) which packages to bundle into the Lambda zip. Lambda cannot install pip packages at runtime.
✔️ Awesome, I've got everything!
Great. Make sure both files are saved in your camay-lambda folder.
ⓧ I'd like to double check the full code
Your lambda_function.py is over 30 lines, so scroll through your file and confirm each section is present in order:
- Lines 1-4: Imports (json, os, genai, types).
- Lines 6-8: GEMINI_API_KEY read from environment and client initialized.
- Lines 10-20: SYSTEM_PROMPT multiline string defining Camay's personality.
- Lines 23-38: lambda_handler body parsing, validation, and contents building.
- Lines 40-55: Gemini API call and return statement.
- Lines 58-64: if __name__ == "__main__" test block.
And requirements.txt contains exactly one line:
google-genai==2.14.0
Camay is alive and responding locally. Next up, you will package this code and deploy it to AWS Lambda so it runs in the cloud.
Package and Deploy to AWS Lambda
In the previous step, you proved Camay works locally. She responds with rehabilitation guidance when you run the script.
The problem is that only you can use her. AWS Lambda lets you run code in the cloud without managing servers, but it cannot install pip packages at runtime. You need to bundle your code and all dependencies into a single zip file and upload it. In this step, you will package everything, create the cloud infrastructure, and deploy your function.
In this step, get ready to:
- Bundle your Python code and dependencies into a deployment zip file.
- Create an IAM execution role so Lambda has permission to run.
- Deploy the Lambda function and test it from the AWS Console.
Install dependencies into a packaging folder
Lambda runs your code in a sandboxed environment. It does not have access to pip at runtime, so every dependency your code imports must be bundled alongside it in the zip file you upload.
- Return to your terminal. Make sure you are inside the camay-lambda folder by running:
cd ~/Desktop/camay-lambda
- Install the google-genai package into a local package/ folder by running:
pip install google-genai==2.14.0 -t package/
What does -t package/ do?
The -t flag tells pip to install into a specific target directory instead of your global Python environment. This isolates the dependencies into a folder you can zip up and send to Lambda.
- Confirm the folder was created by running:
ls package/
You should see folders like google/, httpx/, and other dependency directories listed.
- Copy your Lambda function code into the package/ folder by running:
copy lambda_function.py package/
Why copy the file into package/?
When Lambda unzips your deployment bundle, it expects to find your handler file at the root level of the zip contents. By copying lambda_function.py into package/, you ensure that when you zip everything inside that folder, both your code and its dependencies live side by side at the top level.
Copy command not working?
On macOS or Linux, use cp lambda_function.py package/ instead. The copy command is Windows-specific.
Help me figure out the right command for my operating system.
Create the IAM role and deploy to Lambda
Before you can create the Lambda function, AWS needs an IAM execution role. This role gives Lambda permission to run your code and write logs to CloudWatch.
- Create a trust policy file that tells AWS "Lambda is allowed to assume this role" by running:
'{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' > trust-policy.json
What is a trust policy?
A trust policy is a JSON document that tells AWS which service is allowed to assume (use) a role. Here, you are granting the Lambda service permission to act with whatever permissions the role carries.
- Create the IAM role using the trust policy by running:
aws iam create-role --role-name camay-lambda-role --assume-role-policy-document file://trust-policy.json
You should see a JSON response containing a Role object with an Arn field. Copy the full ARN value. It looks like arn:aws:iam::123456789012:role/camay-lambda-role (with your account number instead).
- Attach the AWSLambdaBasicExecutionRole policy so your function can write logs by running:
aws iam attach-role-policy --role-name camay-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
What is AWSLambdaBasicExecutionRole?
This is an AWS-managed policy that grants your Lambda function permission to create log groups and write log events to CloudWatch. Every Lambda function needs this at minimum so you can see what happened during each invocation.
- Now create the deployment zip. Move into the package/ folder and zip its contents by running:
cd package
powershell Compress-Archive -Path * -DestinationPath ../camay-lambda.zip
- Move back to your project folder by running:
cd ..
- Confirm the zip was created by running:
dir camay-lambda.zip
You should see camay-lambda.zip listed in the output.
Compress-Archive not recognized?
This command is PowerShell-specific. If you are on macOS or Linux, use zip -r ../camay-lambda.zip . instead while inside the package/ folder.
Help me figure out the right zip command for my operating system.
- Deploy the Lambda function to AWS by running the following command. Replace the role ARN with the one you copied earlier:
aws lambda create-function --function-name camay-chatbot --runtime python3.13 --role [[ROLE_ARN="arn:aws:iam::123456789012:role/camay-lambda-role"]] --handler lambda_function.lambda_handler --zip-file fileb://camay-lambda.zip --timeout 30 --memory-size 256
What do these flags mean?
- --function-name camay-chatbot names your function in AWS.
- --runtime python3.13 tells Lambda which Python version to use.
- --handler lambda_function.lambda_handler tells Lambda to look in lambda_function.py for a function called lambda_handler.
- --timeout 30 gives the function 30 seconds to respond (enough time for the Gemini API call).
- --memory-size 256 allocates 256 MB of memory to handle the SDK and network calls.
You should see a JSON response with details about your new function, including FunctionName, Runtime, and State.
Seeing an InvalidParameterValueException about the role?
IAM roles take a few seconds to propagate after creation. Wait 10 seconds and run the aws lambda create-function command again.
Also double-check that you pasted the full ARN from the create-role output. It must start with arn:aws:iam:: and end with :role/camay-lambda-role.
Help me troubleshoot this Lambda creation error.
Set the API key and test in the console
Your function is deployed, but it cannot call Gemini yet. It reads the API key from an environment variable, and you have not set that variable on the Lambda function.
- Add your Gemini API key as a Lambda environment variable by running:
aws lambda update-function-configuration --function-name camay-chatbot --environment "Variables={GEMINI_API_KEY=your-api-key-here}"
Why set the key this way?
Environment variables keep secrets out of your source code. Lambda encrypts them at rest and injects them into your function's runtime. This is the same pattern used in production AI services to manage API keys.
Before you test, what do you think will happen when Lambda runs your function with a sample message? Will it succeed or fail?
- Open the AWS Lambda console in your browser.
- Click on your camay-chatbot function.
- Click the Test tab.
- Replace the default event JSON with:
{"message": "I had a stroke last month and feel overwhelmed"}
- Click Test.
You should see a green "Execution result: succeeded" banner. Expand the details and you will see Camay's JSON response with rehabilitation guidance.
Seeing a timeout or module import error?
A timeout means the Gemini API call took longer than 30 seconds. Try clicking Test again. If you see a module not found error, your zip file structure may be wrong. The google/ folder and lambda_function.py must both be at the root of the zip, not nested inside another folder.
Help me debug my Lambda function test failure.
Your function runs in the cloud. You can see Camay respond from the Lambda console. But try sharing that with someone else. There is no URL, no HTTP endpoint, no way to call it from outside AWS. The function is trapped inside your account. In the next step, you will give Camay a front door with API Gateway.
Expose Camay with API Gateway
Your Lambda function works. You proved it in the console test. But right now, Camay is trapped inside AWS with no public URL.
In this step, you will give Camay a front door using Amazon API Gateway. API Gateway creates a public HTTPS endpoint that routes incoming HTTP requests to your Lambda function. This is the gap between "it runs" and "it's deployed."
In this step, get ready to:
- Create a REST API in API Gateway with a /chat resource and POST method.
- Deploy the API to a prod stage and get a public invoke URL.
- Test Camay over the public internet with a curl command.
Create the REST API and /chat resource
API Gateway organizes endpoints as resources (URL paths) with methods (HTTP verbs like POST). You need to create the API itself, find its root resource, create a /chat child resource, and wire up a POST method that talks to your Lambda.
- Create the REST API by running this command:
aws apigateway create-rest-api --name "CamayAPI" --endpoint-configuration types=REGIONAL
What does this command do?
This creates a new REST API named CamayAPI with a REGIONAL endpoint type. REGIONAL means it is optimized for clients in the same AWS region.
The output includes an id field. This is your API ID. Copy it and save it somewhere. You will use it in every command that follows.
- Get the root resource ID by running:
aws apigateway get-resources --rest-api-id [[API_ID="your API ID from the previous command"]]
What does this output show?
The output shows an items array with at least one resource whose path is /. The id of that root resource is what you need for the next command. Copy it.
- Create the /chat resource under the root by running:
aws apigateway create-resource --rest-api-id [[API_ID="your API ID"]] --parent-id [[ROOT_RESOURCE_ID="your root resource ID"]] --path-part chat
What does this command do?
This creates a new resource at the path /chat under the root. The output includes the new resource's id. Copy this value. It is the resource ID you will attach the POST method to.
- Create a POST method on the /chat resource by running:
aws apigateway put-method --rest-api-id [[API_ID="your API ID"]] --resource-id [[CHAT_RESOURCE_ID="your /chat resource ID"]] --http-method POST --authorization-type NONE
- Connect the POST method to your Lambda function using Lambda proxy integration by running:
aws apigateway put-integration --rest-api-id [[API_ID="your API ID"]] --resource-id [[CHAT_RESOURCE_ID="your /chat resource ID"]] --http-method POST --type AWS_PROXY --integration-http-method POST --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:[[AWS_ACCOUNT_ID="your AWS account ID"]]:function:camay-chatbot/invocations
What is happening here?
The --type AWS_PROXY flag tells API Gateway to forward the entire HTTP request (headers, body, path) straight into your Lambda's event parameter. Your handler already knows how to parse event["body"] because you wrote it that way in Step 2.
The --integration-http-method POST is required for all Lambda integrations. AWS invokes Lambda via POST regardless of the client-facing HTTP method.
- Grant API Gateway permission to invoke your Lambda function by running:
aws lambda add-permission --function-name camay-chatbot --statement-id apigateway-invoke --action lambda:InvokeFunction --principal apigateway.amazonaws.com --source-arn "arn:aws:execute-api:us-east-1:[[AWS_ACCOUNT_ID="your AWS account ID"]]:[[API_ID="your API ID"]]/*/*"
Why is this permission needed?
When you create an integration via the CLI, API Gateway does not automatically get permission to call your function. This command adds a resource-based policy to the Lambda function allowing apigateway.amazonaws.com to invoke it. Without this step, every API call returns a 500 Internal Server Error.
Getting an error about the statement ID?
If you see a message saying the statement ID already exists, it means you ran the command before. You can safely ignore this error or use a different --statement-id value.
If you get an "Invalid ARN" error, double-check that your API ID and account ID are correct with no extra spaces.
Need help debugging?
Deploy the API to a prod stage
Your API is configured but not yet reachable. You need to deploy it to a stage, which makes it callable over the internet.
- Deploy the API to a stage called prod by running:
aws apigateway create-deployment --rest-api-id [[API_ID="your API ID"]] --stage-name prod
What is your invoke URL?
After deploying, your API is live at a URL following this pattern:
https://<api-id>.execute-api.us-east-1.amazonaws.com/prod/chat
Replace <api-id> with your actual API ID. Save this full URL. You will use it to test in the next substep.
Test Camay over the public internet
This is the moment of truth. Before you run the test command, ask yourself: do you think this request will succeed and return Camay's AI response? What could still go wrong?
- Test your live API by running this curl command:
curl -X POST https://[[API_ID="your API ID"]].execute-api.us-east-1.amazonaws.com/prod/chat -H "Content-Type: application/json" -d "{\"message\": \"What exercises can help with my arm mobility after stroke?\"}"
You should see a JSON response with Camay's rehabilitation-focused answer and the model field showing gemini-2.5-flash. Camay is now live on the internet.
Getting a 500 Internal Server Error?
This usually means API Gateway cannot invoke your Lambda function. Confirm you ran the aws lambda add-permission command successfully. Check that the account ID and API ID in the --source-arn are correct.
If you see a 403 Forbidden or Missing Authentication Token error, verify your invoke URL matches the exact pattern with /prod/chat at the end.
Still stuck?
Your AI chatbot is live on a public URL. Anyone with the URL can now talk to Camay. Next up, you will lock the door so only authorized users can call your API.
Secure the API with an API Key
In the previous step you gave Camay a public front door. Anyone who discovers that URL can call it and burn through your Gemini API credits.
Adding an API Gateway API key ensures only authorized clients can reach the endpoint. This is a real production concern for any deployed AI service that calls a paid model.
In this step, get ready to:
- Create a usage plan and API key in API Gateway.
- Require the API key on the POST method for /chat.
- Verify that unauthenticated requests are rejected with 403 Forbidden.
Create a usage plan and API key
API Gateway uses usage plans to control who can call your API and at what rate. An API key links a caller to a plan.
- Create the usage plan by running this command (replace the API ID with yours from the previous step):
aws apigateway create-usage-plan --name "CamayPlan" --api-stages apiId=[[API_ID="your-rest-api-id"]],stage=prod
What does this command do?
- It creates a usage plan called CamayPlan and associates it with the prod stage of your API.
- The response includes an id field. Copy and save that value. You will need it in the next command.
- Create an API key by running:
aws apigateway create-api-key --name "CamayKey" --enabled
What does this command do?
This generates a new API key called CamayKey and enables it immediately. The response includes both an id and a value field. Copy the id for the next command and save the value for testing later.
- Associate the API key with your usage plan by running:
aws apigateway create-usage-plan-key --usage-plan-id [[USAGE_PLAN_ID="your-usage-plan-id"]] --key-type "API_KEY" --key-id [[KEY_ID="your-api-key-id"]]
What does this command do?
This links your API key to the usage plan. Only keys associated with the plan can access the API stages covered by that plan.
- Confirm the association succeeded. The response should include your key's id and a type of API_KEY.
Seeing a NotFoundException?
Double-check that the --usage-plan-id and --key-id values match exactly what the previous commands returned. IDs are short alphanumeric strings.
Help me figure out which ID is wrong
Require the API key on the POST method
The usage plan exists, but API Gateway does not enforce the key unless you tell the specific method to require it.
- Update the POST method on /chat to require an API key by running:
aws apigateway update-method --rest-api-id [[API_ID="your-rest-api-id"]] --resource-id [[RESOURCE_ID="your-chat-resource-id"]] --http-method POST --patch-operations op="replace",path="/apiKeyRequired",value="true"
What does this command do?
The update-method command patches the method's apiKeyRequired flag to true. After this, any request to POST /chat without a valid x-api-key header will be rejected.
Finding your resource ID
If you did not save the resource ID from Step 4, retrieve it by running:
aws apigateway get-resources --rest-api-id [[API_ID="your-rest-api-id"]]
Look for the item whose pathPart is chat and copy its id.
- Redeploy the API to the prod stage so the change takes effect:
aws apigateway create-deployment --rest-api-id [[API_ID="your-rest-api-id"]] --stage-name prod
- You should see a response containing a new deployment id and createdDate. This confirms the updated configuration is live.
Deployment returned an error?
Make sure you pass the same --rest-api-id and that the stage name is exactly prod (case-sensitive).
Help me debug my redeployment error
Verify the API key is enforced
Before you run the test, predict what you expect to happen when you call the API without any key header. Will you get a successful response or an error?
- Call the API without the key by running:
curl -X POST [[INVOKE_URL="https://your-api-id.execute-api.us-east-1.amazonaws.com/prod/chat"]] -H "Content-Type: application/json" -d "{\"message\": \"What exercises can help with my arm mobility after stroke?\"}"
You should see a 403 Forbidden response. That confirms the API now rejects requests that lack a valid API key.
- Now call the API with the key by adding the x-api-key header:
curl -X POST [[INVOKE_URL="https://your-api-id.execute-api.us-east-1.amazonaws.com/prod/chat"]] -H "Content-Type: application/json" -H "x-api-key: your-api-key-value-here" -d "{\"message\": \"What exercises can help with my arm mobility after stroke?\"}"
This time you should see Camay's JSON response with rehabilitation guidance, just like before. The key proves you are an authorized caller.
Still getting 403 with the key?
- Make sure you are using the API key value (the long alphanumeric string), not the key's id. They are different fields in the create-api-key response.
- Confirm you redeployed the API after changing apiKeyRequired. Changes do not take effect until you create a new deployment.
- It can take a minute or two for newly created API keys to propagate. Wait 60 seconds and retry.
Help me debug my 403 error
Your API is now locked down. Only callers who present the correct API key can reach Camay. You have a fully deployed, secured, serverless AI chatbot on AWS.
Secret mission
Add Conversation Memory with DynamoDB
Camay currently forgets everything between requests. Each call is stateless. In this mission, you connect her to DynamoDB so she remembers past messages across requests, completing the serverless architecture.
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. This project uses AWS Lambda (Always Free tier), API Gateway (12-month free tier), and optionally DynamoDB (Always Free tier).
Cost warning
API Gateway's free tier (1 million calls/month) expires after 12 months. If you leave the API deployed beyond that window and it receives traffic, you will incur charges. Lambda and DynamoDB remain in the Always Free tier indefinitely for low-traffic usage.
Resources you used:
- AWS Lambda function camay-chatbot (Always Free tier: 1M requests/month, 400,000 GB-seconds/month).
- API Gateway REST API CamayAPI with usage plan CamayPlan and API key (12-month free tier: 1M calls/month).
- IAM role camay-lambda-role (no cost).
- DynamoDB table camay-conversations (Always Free tier: 25 GB storage, 25 RCU/WCU). Only if you completed the Secret Mission.
- Local project folder camay-lambda on your Desktop.
Keep everything running
No action needed. Choose this if you are still actively building or want to keep the API available for testing.
- All resources stay within free tier for low-traffic usage. No ongoing charges expected while Lambda and DynamoDB remain in the Always Free tier.
- API Gateway's free tier lasts 12 months from your AWS account creation date. After that, calls are billed per million.
- Your local camay-lambda folder stays on your Desktop.
Pause - I'll come back to this later
Shut down active resources to stop any potential charges while keeping your code and configuration intact.
- Lambda and API Gateway do not charge when idle. If no requests hit your endpoint, you pay nothing.
- DynamoDB charges nothing while data stays under 25 GB and you stay under 25 read/write capacity units.
- No pause action is needed. These services are already effectively paused when not receiving traffic.
Delete - I don't want to use this again
Remove all project resources and start fresh. Run each command below in your terminal.
- Delete the Lambda function by running:
aws lambda delete-function --function-name camay-chatbot
- Delete the API Gateway REST API. Replace the API ID with the one you noted when you created it:
aws apigateway delete-rest-api --rest-api-id [[API_ID="your REST API ID"]]
- Detach the managed policy from the IAM role, then delete any inline policies, then delete the role itself:
aws iam detach-role-policy --role-name camay-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name camay-lambda-role
What if the role has inline policies?
If you added a DynamoDB inline policy to the role during the Secret Mission, you need to delete it before deleting the role. List inline policies with:
aws iam list-role-policies --role-name camay-lambda-role
Then delete each one by name:
aws iam delete-role-policy --role-name camay-lambda-role --policy-name [[POLICY_NAME="your inline policy name"]]
- If you completed the Secret Mission, delete the DynamoDB table:
aws dynamodb delete-table --table-name camay-conversations
- Delete the local project folder. On Windows:
Remove-Item -Recurse -Force ~\Desktop\camay-lambda
- On macOS or Linux:
rm -rf ~/Desktop/camay-lambda
That's a Wrap!
That's a Wrap!
Nice work! You've just taken a local AI prototype and deployed it as a live, public serverless API on AWS.
You've learned how to:
- Built and deployed a Python Lambda function that wraps Google Gemini with a custom rehabilitation system prompt, packaged with its dependencies and shipped from the CLI.
- Created a public REST API with API Gateway that routes HTTP requests to your Lambda function, giving Camay a live HTTPS endpoint anyone can call.
- Secured the endpoint with an API key and usage plan so only authorized clients can access your chatbot and rack up Gemini costs.
- (Secret Mission) Added persistent conversation memory using DynamoDB, allowing Camay to remember past messages across requests and completing a fully serverless AI chatbot architecture.
Ready to quiz yourself?