Build a Serverless Help Desk API
Wire up Lambda, DynamoDB, and API Gateway to build a live REST API.
Introduction
30 Second Summary
Every company has an IT help desk. When something breaks, you submit a support ticket and someone resolves it.
In this project, you will build that exact system in the cloud using AWS. You will create, track, and resolve support tickets through a live API that runs without any servers.
What You'll Build
Picture opening your terminal, sending a request to a URL, and watching a brand-new support ticket land in your cloud database in under a second.
By the end of this project, you'll have:
- A live REST API URL that you can hit from any browser or terminal to create new support tickets in the cloud.
- A NoSQL database on AWS that stores every ticket and tracks its status from open to resolved.
- A complete serverless back end that handles creating, listing, and resolving tickets. You never manage a single server.
- Secret Mission: Add priority-based filtering so you can list only high-priority open tickets.
Are there any prerequisites?
This project walks you through everything from scratch, including creating your AWS account. No programming experience is needed.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your AWS Account and Create Your Ticket Database
Your help desk needs two things before you can write any code. An AWS account to host your cloud services, and a database to store the tickets themselves.
In this step, you will create an AWS account on the Free Tier and set up a DynamoDB table to hold your tickets. DynamoDB is a NoSQL database that scales automatically, making it the standard choice for serverless projects.
In this step, get ready to:
- Create an AWS Free Tier account.
- Create a DynamoDB table to store support tickets.
- Verify the table is active and ready to use.
Create your AWS Free Tier account
AWS offers a Free plan for new customers that gives you credits to explore services at no cost. Every service in this project fits within those free limits.
✔️ I already have an AWS account
- Sign in to the AWS Management Console at console.aws.amazon.com
You should see the Console Home page. You are all set to continue.
ⓧ I need to create one
- Open the AWS Free Tier page in your browser.
- Click Create an AWS Account.
- Enter your email address and choose an account name, then click Verify email address.
- Check your email for a verification code, enter it, and set a strong password.
- When asked to choose an account plan, select the Free plan.
- Enter your contact information and accept the AWS Customer Agreement.
- Enter your billing information. AWS requires a credit or debit card for identity verification. You will not be charged for the resources in this project.
- Complete the identity verification step by entering the code sent to your phone.
- Choose a support plan. The free Basic Support plan is fine for this project.
- Click Complete sign up. Account activation usually takes a few minutes but can take up to 24 hours.
- Once activated, sign in to the AWS Management Console.
You should see the Console Home page after signing in.
Account not activating?
Activation usually takes a few minutes. Check your email inbox and spam folder for a confirmation message from AWS. If your account is still not activated after 24 hours, contact AWS Support.
Help me troubleshoot my AWS account activation.
What costs will I pay?
New AWS customers receive up to $200 in credits on the Free plan. The three services in this project all have generous free tier allowances:
- DynamoDB: 25 GB of storage per month (always free).
- Lambda: 1 million requests per month (always free).
- API Gateway: 1 million REST API calls per month (free for 12 months from sign-up).
This project stays well within these limits. You will pay nothing to complete it.
Create a DynamoDB table to store tickets
DynamoDB stores data as items in tables. Each item needs a unique key to identify it. You will create a table called helpdesk-tickets where every support ticket gets a unique ticket_id.
- Open the DynamoDB console at console.aws.amazon.com/dynamodb.
- In the left navigation pane, click Tables.
- Click Create table.
- In the Table name field, enter helpdesk-tickets.
- In the Partition key field, enter ticket_id.
- Leave the partition key type dropdown set to String.
- Under Table settings, keep Default settings selected.
- Click Create table.
What is a partition key?
A partition key is the unique identifier for each item in a DynamoDB table. Every item you store must have a unique value for this key.
In your help desk, each ticket will have its own ticket_id so you can look it up, update it, or delete it individually.
Your table will show a status of Creating for a few seconds, then switch to Active.
Table stuck on Creating?
DynamoDB tables usually become active within a few seconds. If yours takes longer, refresh the page. If you see an error, check that you typed helpdesk-tickets as the table name with a hyphen, not a space.
Help me troubleshoot my DynamoDB table creation.
Verify the table exists
Before you move on, think about what you expect to see when you click on the table name. What status should a ready-to-use table show?
- In the DynamoDB console, click the helpdesk-tickets table name in the tables list.
You should see the table details page with a status of Active and a partition key of ticket_id (String). Your database is live and ready to receive tickets.
Your database is ready and waiting for tickets. Next up, you will write a Lambda function that takes ticket submissions and stores them in this table.
Write Your First Lambda Function to Create Tickets
Your DynamoDB table is live and ready to store tickets. Right now, though, it is just an empty container with no way to add data to it.
In this step, you will write a Lambda function in Python that takes a ticket submission and stores it in your database. Lambda lets you run code in the cloud without managing any servers. You will also set up the IAM permissions that allow your function to talk to DynamoDB.
In this step, get ready to:
- Create an IAM policy and role that grants your Lambda function permission to use DynamoDB.
- Write a Lambda function that creates support tickets.
- Test the function and verify a ticket appears in your database.
Create an IAM policy and role for Lambda
Before your Lambda function can read or write data in DynamoDB, it needs explicit permission. AWS controls access through IAM policies and roles. You will create a policy that spells out exactly which database actions are allowed, then wrap it in a role that Lambda can assume.
- Open the IAM console at https://console.aws.amazon.com/iam/.
- In the left navigation pane, choose Policies.
- Choose Create policy.
- In the Policy editor section, choose the JSON option.
- Select all of the default text in the editor and replace it with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Scan",
"dynamodb:Query"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
What does this policy do?
The first statement grants your function permission to create, read, update, delete, scan, and query items in DynamoDB. These are the core database operations your help desk needs.
The second statement allows the function to write logs to CloudWatch. This is essential for debugging because Lambda sends all of its output to CloudWatch Logs.
- Choose Next.
- On the Review and create page, enter helpdesk-lambda-policy in the Policy name field.
- Choose Create policy.
You should see a success banner confirming that helpdesk-lambda-policy has been created.
Now you need a role that uses this policy. A role is like a badge that Lambda puts on when it runs your function, giving it the permissions the policy defines.
- In the left navigation pane, choose Roles.
- Choose Create role.
- For Trusted entity type, choose AWS service.
- For Service or use case, choose Lambda.
- Choose Next.
- In the search box under Permissions policies, type helpdesk-lambda-policy and select the checkbox next to it.
- Choose Next.
- For Role name, enter helpdesk-lambda-role.
- Choose Create role.
You should see a success banner confirming that helpdesk-lambda-role has been created.
Don't see your policy when creating the role?
Make sure you type the full name helpdesk-lambda-policy in the search box. The policy list only filters as you type.
If the policy still does not appear, go back to Policies in the left navigation and confirm it was created successfully.
help me find my IAM policy when creating a Lambda role
Create the Lambda function
With permissions in place, you can create the Lambda function itself. You will write Python code directly in the Lambda console's built-in editor.
- Open the Lambda console at https://console.aws.amazon.com/lambda/home#/functions.
- Choose Create function.
- Select Author from scratch.
- For Function name, enter helpdesk-ticket-api.
- For Runtime, choose Python 3.13.
- Expand Change default execution role under Permissions.
- Choose Use an existing role.
- Select helpdesk-lambda-role from the dropdown.
- Choose Create function.
Lambda creates your function and opens the code editor with a default lambda_function.py file.
- Select all the default code in lambda_function.py and replace it with the following imports and handler function:
import json
import boto3
from datetime import datetime, timezone
# Create the DynamoDB resource
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('helpdesk-tickets')
def lambda_handler(event, context):
# If the event comes from API Gateway, parse the body
if 'body' in event:
body = json.loads(event['body'])
else:
body = event
operation = body.get('operation')
if operation == 'create':
result = create_ticket(body.get('payload', {}))
else:
result = {'error': f'Unknown operation: {operation}'}
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps(result, default=str)
}
What does this code do?
- boto3 is the AWS SDK for Python. It comes pre-installed in the Lambda runtime, so you do not need to install anything.
- dynamodb.Table('helpdesk-tickets') connects to the DynamoDB table you created in Step 1.
- lambda_handler is the entry point Lambda calls every time the function runs. It reads an operation field from the incoming event and routes it to the right helper function.
- The if 'body' in event check lets the function work with both direct test events and API Gateway requests later.
- Add the following function below lambda_handler:
def create_ticket(payload):
item = payload.get('Item', {})
item['status'] = 'open'
item['created_at'] = datetime.now(timezone.utc).isoformat()
table.put_item(Item=item)
return {'message': 'Ticket created', 'ticket': item}
What does create_ticket do?
This function takes the ticket data from the request, automatically sets its status to open, stamps it with the current time, and writes it to DynamoDB using put_item.
- In the DEPLOY section, choose Deploy.
You should see a banner confirming that the function has been updated.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file by clicking Deploy again. If it is grayed out, your code is already deployed.
ⓧ I'd like to double check the full code
Your lambda_function.py should contain two sections in this order:
- The imports, DynamoDB setup, and lambda_handler function from the first code block above.
- The create_ticket function from the second code block above.
Scroll up and confirm both sections are present in your code editor, then click Deploy.
Seeing an error after deploying?
Make sure the create_ticket function is defined outside of lambda_handler. It should be at the same indentation level, not nested inside the handler.
Also check that the table name in your code matches exactly: helpdesk-tickets.
help me fix my Lambda function deployment error
Test the function
Your function is deployed. Now you will send it a test event to confirm it can create a ticket in DynamoDB.
- Choose the Test tab.
- Scroll down to the Event JSON section and replace the default event with the following:
{
"operation": "create",
"payload": {
"Item": {
"ticket_id": "TICKET-001",
"title": "Laptop won't connect to WiFi",
"description": "Cannot find the office network"
}
}
}
What is this test event?
This JSON simulates a request to create a new support ticket. The operation field tells your handler which action to run. The payload contains the ticket data, including a unique ticket_id, a title, and a description.
Before you run the test, take a moment to predict what you expect to happen. Will the function succeed or fail? What should the response contain?
- Choose Test.
Under Executing function: succeeded, expand Details. You should see a response containing your ticket data with status set to open and a created_at timestamp.
Now confirm the ticket actually landed in your database.
- Open the DynamoDB console at https://console.aws.amazon.com/dynamodb/.
- In the left navigation, choose Tables, then click your helpdesk-tickets table.
- Choose Explore table items.
You should see your test ticket with ticket_id of TICKET-001, status of open, and a created_at timestamp. Your Lambda function is working.
Test failed or no ticket in DynamoDB?
If you see an AccessDeniedException error, your Lambda function does not have the right permissions. Go back to the Lambda console, open your function's Configuration tab, choose Permissions, and confirm the execution role is helpdesk-lambda-role.
If the test succeeded but no item appears in DynamoDB, make sure you are looking at the helpdesk-tickets table in the same region as your Lambda function.
help me debug my Lambda function test failure
Your Lambda function can create tickets and store them in DynamoDB. Next up, you will add the ability to list all tickets and discover a key limitation of your current setup.
Add Listing, But Discover You Cannot Access It From Outside AWS
Your Lambda function can create tickets, but a help desk where you cannot see the queue is not much help. A support team needs to view all open tickets at a glance to know what needs attention.
In this step, you will add a list operation that scans your entire DynamoDB table and returns every ticket. Then you will test it and see what happens when you try to share your help desk with someone else.
In this step, get ready to:
- Update your Lambda function to handle a list operation.
- Test the list operation inside the Lambda console.
- Discover what happens when you try to access your function from outside AWS.
Update your Lambda code to handle multiple operations
Right now your function only handles a create operation. You need to add a second branch that responds to a list operation by scanning the DynamoDB table.
- Return to your helpdesk-ticket-api function in the Lambda console.
- In the Code source editor, find the if/else block inside lambda_handler that currently looks like this:
if operation == 'create':
result = create_ticket(body.get('payload', {}))
else:
result = {'error': f'Unknown operation: {operation}'}
- Add an elif branch for the list operation between create and else, so the block now looks like this:
if operation == 'create':
result = create_ticket(body.get('payload', {}))
elif operation == 'list':
result = list_tickets()
else:
result = {'error': f'Unknown operation: {operation}'}
The list_tickets() function does not exist yet. You will add it next.
- Scroll down to the bottom of the file, below the create_ticket function, and add the list_tickets function by pasting this code:
def list_tickets():
# Scan the entire DynamoDB table for all tickets
response = table.scan()
return {'tickets': response.get('Items', [])}
What does this code do?
table.scan() reads every item in your helpdesk-tickets DynamoDB table and returns them all at once. For a small learning project like this, scanning the full table is fine.
The response from DynamoDB includes a key called Items that holds the list of tickets. response.get('Items', []) returns an empty list if the table has no tickets, so your function never crashes on an empty table.
- In the DEPLOY section above the code editor, click Deploy to save and publish your updated function.
Seeing a syntax error after deploying?
- Check that your elif line is indented at the same level as the if and else lines. Python is strict about indentation.
- Make sure the list_tickets function starts at the very left edge of the file (no indentation), just like create_ticket above it.
- Confirm that the function name in the elif branch (list_tickets()) matches the function definition exactly.
- Still stuck? Help me fix a syntax error in my Lambda function after adding a list operation.
✔️ Awesome, I've got everything!
Great. Make sure you have clicked Deploy and the editor is not showing any errors.
ⓧ I'd like to double check the full code
Scroll through your code in the Lambda console editor and confirm these two changes are present, in order:
- An elif operation == 'list' branch inside lambda_handler, between the create block and the else block.
- A list_tickets() function defined below create_ticket, containing table.scan() and a return statement with response.get('Items', []).
Test the list operation
Your function now knows how to handle a list operation. Time to send it one and see what comes back.
- Click the Test tab in the Lambda console.
- Under Test event, choose Create new event.
- Enter listTest as the event name.
- Replace the default JSON in the Event JSON section with this test payload:
{
"operation": "list"
}
Why is this event so simple?
The list operation does not need any extra data. It just scans the whole table and returns everything. The only thing your function needs to know is which operation to run.
Before you run this, think about what you expect to see. How many tickets are in your table? What status should they have?
- Click Test.
- Under Execution result, expand Details.
You should see a response containing all the tickets you created in the previous step, with their ticket_id, status, and created_at fields. The list operation works.
Seeing an error instead of tickets?
- Make sure you clicked Deploy before running the test. Undeployed code changes are not active.
- Check that your test event JSON is exactly {"operation": "list"} with no extra characters or missing quotes.
- Still stuck? Help me debug my Lambda list operation test.
Try to access your function from outside AWS
Your list operation works. Now imagine you are a support team lead, and you want your team to check the ticket queue from their own computers.
- Look around the Lambda console page for your helpdesk-ticket-api function.
- Try to find a URL, endpoint, or link you could send to a colleague so they could call your function from a browser or terminal.
There is not one. Your Lambda function can only be triggered from the Test tab inside the AWS Console. Nobody outside of your AWS account can reach it.
Why does this matter?
A help desk that only the developer can access defeats the purpose. In the real world, support agents, automated monitoring systems, and even web forms all need to reach your function over the internet.
Right now, your function is locked behind the AWS Console login. You need something that gives your Lambda function a public URL. That is exactly what the next step solves.
Your help desk can create and list tickets, but it is trapped inside the AWS Console. In the next step, you will give it a public URL that anyone can call from a browser or terminal.
Expose Your Help Desk to the World With API Gateway
In the last step, you discovered a frustrating limitation. Your Lambda function can create and list tickets perfectly, but only from inside the AWS Console.
Amazon API Gateway fixes this by putting a public URL in front of your Lambda function. Anyone on the internet can send an HTTP request to that URL, and API Gateway routes it straight to your function. In this step, you will create a REST API and get a live URL you can call from any terminal.
In this step, get ready to:
- Create a REST API with a resource and method that connects to your Lambda function.
- Deploy the API and grab a live Invoke URL.
- Test the live API from your terminal using curl.
Create a REST API in API Gateway
API Gateway organizes your API into resources (URL paths like /tickets) and methods (HTTP verbs like POST). You will create an API, add a /tickets resource, and attach a POST method that sends requests to your Lambda function.
- Open the API Gateway console at https://console.aws.amazon.com/apigateway.
- Choose Create API.
- Find the REST API card (not the one labelled "Private") and choose Build.
- For API name, enter HelpdeskAPI.
- Leave API endpoint type set to Regional.
- Choose Create API.
You should see your new API with a single / (root) resource in the Resources pane.
- Choose Create resource.
- For Resource name, enter tickets.
- Choose Create Resource.
You should now see /tickets listed under the root / resource. This is the URL path that clients will use to reach your help desk.
- Select the /tickets resource in the Resources pane.
- Choose Create method.
- For Method type, select POST.
- For Integration type, select Lambda function.
- For Lambda function, select the region where you created your function, then choose helpdesk-ticket-api.
- Choose Create method.
What about permissions?
When you create the method, API Gateway automatically adds a resource-based permission to your Lambda function so that API Gateway can invoke it. The console handles this for you behind the scenes.
Your POST method is now wired to your Lambda function. Before deploying, test it right here in the console to make sure the connection works.
- Choose the Test tab. You may need to choose the right arrow button to reveal it.
- Leave Query strings and Headers empty.
- In the Request body field, paste the following JSON:
{
"operation": "create",
"payload": {
"Item": {
"ticket_id": "TICKET-200",
"description": "Email not syncing"
}
}
}
- Choose Test.
The results should show status 200. The response body should contain your ticket with status open.
Seeing a 500 error?
- Make sure you selected helpdesk-ticket-api as the Lambda function, not a different function.
- Confirm you deployed the latest code in Step 3. Return to the Lambda console and choose Deploy if needed.
- Check that the request body is valid JSON with no trailing commas.
Help me debug a 500 error when testing my API Gateway Lambda integration.
Deploy the API and get your live URL
The test you just ran worked inside the console, but the API is not accessible from the outside world yet. You need to deploy it to a stage to get a public URL.
- Choose Deploy API.
- For Stage, select New stage.
- For Stage name, enter test.
- Choose Deploy.
The console takes you to the Stage details pane. You should see an Invoke URL at the top.
- Choose the copy icon next to the Invoke URL to copy it.
- Record your Invoke URL here: your Invoke URL.
What is an Invoke URL?
This is the public address for your API on the internet. It follows the pattern https://<id>.execute-api.<region>.amazonaws.com/test. Anyone with this URL can send requests to your help desk API.
Test your live API from a terminal
Your API now has a real URL on the internet. Time to call it from outside the AWS Console and prove that anyone can create a ticket.
Before you run this command, what do you think will happen? Will your Lambda function receive the request and create a new ticket in DynamoDB?
- Open a terminal on your machine by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
Windows users: use curl.exe
On Windows, PowerShell has a built-in alias that maps curl to a different command. Use curl.exe instead to run the real curl tool.
- Create a ticket through your live API by running this command:
macOS
curl -X POST [[INVOKE_URL="your Invoke URL"]]/tickets -H "Content-Type: application/json" -d '{"operation": "create", "payload": {"Item": {"ticket_id": "TICKET-300", "description": "VPN connection failing"}}}'
Windows
curl.exe -X POST [[INVOKE_URL="your Invoke URL"]]/tickets -H "Content-Type: application/json" -d '{"operation": "create", "payload": {"Item": {"ticket_id": "TICKET-300", "description": "VPN connection failing"}}}'
You should see a JSON response containing "message": "Ticket created" and the ticket details. Your Lambda function just processed a request from outside the AWS Console for the first time.
- Confirm the ticket landed in your database by switching back to the DynamoDB console.
- Choose your helpdesk-tickets table, then choose Explore table items.
- You should see TICKET-300 in the list with status open.
Seeing a connection error or unexpected response?
- Make sure you appended /tickets to the end of your Invoke URL. The full endpoint should look like https://<id>.execute-api.<region>.amazonaws.com/test/tickets.
- On Windows, make sure you used curl.exe instead of curl.
- Check that the JSON in your -d flag is wrapped in single quotes and has no stray characters.
Help me troubleshoot my curl request to API Gateway.
Your help desk API is live on the internet. In the next step, you will add the ability to resolve tickets and close the loop on your help desk workflow.
Add Ticket Resolution to Complete the Workflow
Your help desk now has a live API URL that anyone can use to create and list tickets. That is a great start, but tickets that stay open forever are not very helpful.
A real help desk needs a way to close the loop. In this step, you will add a resolve operation to your Lambda function so tickets can be marked as done.
In this step, get ready to:
- Add a resolve operation to your Lambda function that updates ticket status in DynamoDB.
- Test the complete create, list, and resolve workflow from your terminal using curl.
Add the resolve operation to your Lambda function
Right now, your lambda_handler function only knows two operations: create and list. You need to teach it a third one.
- Return to the Lambda console and open your helpdesk-ticket-api function.
- Select the Code tab and open lambda_function.py in the code editor.
- In the lambda_handler function, find these lines:
elif operation == 'list':
result = list_tickets()
else:
result = {'error': f'Unknown operation: {operation}'}
- Add a new elif branch for the resolve operation between the list block and the else block, so the code now looks like this:
elif operation == 'list':
result = list_tickets()
elif operation == 'resolve':
result = resolve_ticket(body.get('payload', {}))
else:
result = {'error': f'Unknown operation: {operation}'}
Now you need the actual resolve_ticket function that this new branch calls.
- Scroll to the bottom of lambda_function.py, below the list_tickets function, and add the following code:
def resolve_ticket(payload):
ticket_id = payload.get('ticket_id')
response = table.update_item(
Key={'ticket_id': ticket_id},
UpdateExpression='SET #s = :resolved, resolved_at = :time',
ExpressionAttributeNames={'#s': 'status'},
ExpressionAttributeValues={
':resolved': 'resolved',
':time': datetime.now(timezone.utc).isoformat()
},
ReturnValues='ALL_NEW'
)
return {'message': 'Ticket resolved', 'ticket': response.get('Attributes', {})}
What does this code do?
This function uses DynamoDB's update_item method to modify an existing ticket in place, instead of replacing the whole item.
- UpdateExpression tells DynamoDB to set the status field to resolved and add a resolved_at timestamp.
- ExpressionAttributeNames maps #s to status. The word status is a reserved word in DynamoDB, so you must use an alias to reference it in expressions.
- ReturnValues='ALL_NEW' tells DynamoDB to return the full updated ticket so you can confirm the changes.
- In the DEPLOY section of the code editor, choose Deploy to update your function's code.
Do I need to redeploy API Gateway?
No. API Gateway already points to your Lambda function. When you deploy updated Lambda code, API Gateway automatically uses the new version on the next request. No extra steps needed.
✔️ Awesome, I've got everything!
Great. Double check you see the Changes deployed confirmation banner at the top of the code editor.
ⓧ I'd like to double check the full code
Your lambda_function.py should now contain all of the following, in order:
- Imports (json, boto3, datetime) and the DynamoDB table setup at the top.
- The lambda_handler function with if/elif branches for create, list, and resolve, plus an else for unknown operations.
- The create_ticket function.
- The list_tickets function.
- The new resolve_ticket function at the very bottom.
Scroll through your code in the Lambda console editor and confirm each section is present. If anything is missing, scroll back up in this guide and re-add it.
Seeing an error after deploying?
- Check that the elif operation == 'resolve': line is indented at the same level as the other elif branches inside lambda_handler.
- Make sure the resolve_ticket function starts at the leftmost indentation level (no extra indentation), just like create_ticket and list_tickets.
- Confirm there is no duplicate else block. You should have exactly one else at the end of the if/elif chain.
Still stuck? Help me fix a deploy error in my Lambda function code.
Test the full workflow from your terminal
Your Lambda function now supports three operations. Time to run through the complete help desk workflow from your terminal to prove it all works end to end.
- Switch back to your terminal (or PowerShell on Windows).
- Create a new ticket by running this command:
macOS
curl -X POST [[INVOKE_URL="your Invoke URL"]]/tickets \
-H "Content-Type: application/json" \
-d '{"operation": "create", "payload": {"Item": {"ticket_id": "T-301", "title": "Laptop not starting", "description": "Screen stays black after pressing power button"}}}'
Windows
curl.exe -X POST [[INVOKE_URL="your Invoke URL"]]/tickets `
-H "Content-Type: application/json" `
-d '{"operation": "create", "payload": {"Item": {"ticket_id": "T-301", "title": "Laptop not starting", "description": "Screen stays black after pressing power button"}}}'
You should see a JSON response with "message": "Ticket created" and the ticket details including "status": "open".
- List all tickets to confirm the new one appears by running this command:
macOS
curl -X POST [[INVOKE_URL="your Invoke URL"]]/tickets \
-H "Content-Type: application/json" \
-d '{"operation": "list"}'
Windows
curl.exe -X POST [[INVOKE_URL="your Invoke URL"]]/tickets `
-H "Content-Type: application/json" `
-d '{"operation": "list"}'
Look for the ticket with ticket_id of T-301 in the response. Its status should be open.
Now for the moment of truth. Before you run the next command, what do you think will happen to the ticket's status field when you send a resolve request?
- Resolve the ticket by running this command:
macOS
curl -X POST [[INVOKE_URL="your Invoke URL"]]/tickets \
-H "Content-Type: application/json" \
-d '{"operation": "resolve", "payload": {"ticket_id": "T-301"}}'
Windows
curl.exe -X POST [[INVOKE_URL="your Invoke URL"]]/tickets `
-H "Content-Type: application/json" `
-d '{"operation": "resolve", "payload": {"ticket_id": "T-301"}}'
You should see "message": "Ticket resolved" and the full ticket with "status": "resolved" and a new resolved_at timestamp.
- List all tickets one final time to confirm the status change stuck:
macOS
curl -X POST [[INVOKE_URL="your Invoke URL"]]/tickets \
-H "Content-Type: application/json" \
-d '{"operation": "list"}'
Windows
curl.exe -X POST [[INVOKE_URL="your Invoke URL"]]/tickets `
-H "Content-Type: application/json" `
-d '{"operation": "list"}'
Find T-301 in the response. Its status should now be resolved instead of open, and you should see a resolved_at timestamp. That confirms the full create, list, and resolve loop is working through your live API.
Getting an error from the resolve command?
- Make sure the ticket_id in your resolve command matches exactly what you used when creating the ticket. If you changed the ID from T-301, use that same ID in the resolve request.
- Confirm you chose Deploy in the Lambda console after adding the new code. If the code is not deployed, your API still runs the old version without the resolve operation.
- On Windows, make sure you are using curl.exe (not curl). PowerShell aliases curl to a different command that uses different syntax.
Still stuck? Help me debug my resolve ticket API call.
Secret mission
Filter Tickets by Priority
In a real help desk, support agents need to find the most urgent tickets fast. In this mission, you will add priority-based filtering to your API so you can list only tickets that match a specific priority level.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on AWS serverless services. All resources fall within the AWS Free Tier for new accounts. The API Gateway free tier expires after 12 months, so decide whether to keep, pause, or delete your resources.
Cost warning
The API Gateway free tier (1 million REST API calls per month) expires 12 months after your AWS sign-up date. After that, API calls will incur standard charges. If you are not planning to use these resources, delete them to avoid unexpected costs.
Resources you used:
- 1 API Gateway REST API (HelpdeskAPI).
- 1 Lambda function (helpdesk-ticket-api).
- 1 DynamoDB table (helpdesk-tickets).
- 1 IAM role (helpdesk-lambda-role) and 1 IAM policy (helpdesk-lambda-policy).
Keep everything running
No action needed. Choose this if you are still actively building or experimenting with your help desk API.
- All four resources cost nothing when idle under the AWS Free Tier.
- DynamoDB and Lambda free tiers are always free (not time-limited). API Gateway's free tier lasts 12 months from sign-up.
- Your AWS account will remain active.
Pause - I'll come back to this later
Serverless resources do not run continuously, so there is nothing to pause. They only incur charges when actively used.
- Your Lambda function, DynamoDB table, and API Gateway REST API sit idle at zero cost until someone sends a request.
- If you want to prevent anyone from calling your API, you can delete the deployment stage. Open the API Gateway console, select HelpdeskAPI, choose Stages in the left navigation, select the test stage, then choose Stage actions, Delete stage. Enter confirm and choose Delete. You can redeploy later to get a new Invoke URL.
Delete - I don't want to use this again
Remove all project resources to start fresh. Follow each step in order.
- Delete the API Gateway REST API: Open the API Gateway console. On the APIs page, select HelpdeskAPI. Choose API actions, then Delete API. In the dialog, enter confirm and choose Delete.
- Delete the Lambda function: Open the Lambda console and go to the Functions page. Select helpdesk-ticket-api. Choose Actions, then Delete. Type confirm in the text input field and choose Delete.
- Delete the DynamoDB table: Open the DynamoDB console. In the left navigation pane, choose Tables. Select the checkbox next to helpdesk-tickets. Choose Delete and confirm the deletion.
- Delete the IAM role: Open the IAM console. In the navigation pane, choose Roles. Select the checkbox next to helpdesk-lambda-role. Choose Delete at the top of the page. Enter the role name to confirm and choose Delete. The console automatically detaches the managed policy from the role.
- Delete the IAM policy: In the IAM console, choose Policies in the navigation pane. Select the radio button next to helpdesk-lambda-policy. Choose Actions, then Delete. Confirm the deletion and choose Delete.
Nice Work!
Nice Work!
Nice work! You just built a fully serverless IT help desk API on AWS that creates, tracks, and resolves support tickets from a live URL anyone can hit.
You've learned how to:
- Build a serverless backend by wiring together DynamoDB for storage, Lambda for compute, and API Gateway for a public REST API, all without managing a single server.
- Set up IAM permissions following the principle of least privilege, granting your Lambda function only the exact DynamoDB and CloudWatch access it needs.
- Test a live cloud API end-to-end from your terminal using curl, creating tickets, listing them, and resolving them through a deployed URL on the internet.
- Secret Mission: Add priority-based filtering using DynamoDB FilterExpression to query only high-priority open tickets.
Ready to quiz yourself?