diff --git a/apigw-data-validation-tf/README.md b/apigw-data-validation-tf/README.md
new file mode 100644
index 000000000..178876d4d
--- /dev/null
+++ b/apigw-data-validation-tf/README.md
@@ -0,0 +1,101 @@
+# Amazon API Gateway data validation models
+
+This pattern creates an Amazon API Gateway that handles simple data validation at the endpoint without invoking the Lambda function when the data validation fails.
+
+Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/apigw-custom-resource-policy](https://serverlessland.com/patterns/apigw-custom-resource-policy)
+
+Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
+
+## Requirements
+
+* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
+* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
+* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
+* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed
+
+## Deployment Instructions
+
+1. Create a new directory, navigate to that directory in a terminal and clone the repository:
+ ```
+ git clone https://github.com/aws-samples/serverless-patterns
+ ```
+1. Change directory to this pattern's directory
+ ```
+ cd serverless-patterns/apigw-data-validation-tf
+ ```
+1. From the command line, initialize Terraform to downloads and install the providers defined in the configuration:
+ ```
+ terraform init
+ ```
+1. From the command line, apply the configuration in the main.tf file:
+ ```
+ terraform apply
+ ```
+1. Note the outputs from the deployment process. These contain the resource names and/or ARNs which are used for testing.
+
+## API Endpoint
+
+After running `terraform apply`, you will see outputs including the API endpoint URL. You'll need this URL for testing. The output will look similar to:
+```
+api_endpoint = "https://xxxxx.execute-api.us-east-1.amazonaws.com/Prod"
+```
+
+Note: When testing, append `/123?order=ORD12345` to this base URL. For example, if your API endpoint is `https://xxxxx.execute-api.us-east-1.amazonaws.com/Prod`, your full testing URL would be:
+```
+`https://xxxxx.execute-api.us-east-1.amazonaws.com/Prod/123?order=ORD12345`
+```
+
+## How it works
+
+The data model is declared in the API Gateway resource. The Lambda function then requires the request body to be validated against this model.
+
+## Testing
+
+After the application is deployed try the following scenarios.
+
+### Create a new vehicle entering valid data:
+```
+curl --location --request POST 'https://t9nde3gpp2.execute-api.us-east-1.amazonaws.com/Prod/123?order=ORD12345' \
+--header 'Content-Type: application/json' \
+--header 'custom-agent: MyMobileApp/1.0' \
+--data-raw '{
+ "make":"MINI",
+ "model":"Countryman",
+ "year": 2010
+}'
+```
+Expected response: `{"message": "Data validation succeded", "data": {"make": "MINI", "model": "Countryman", "year": 2010}}`
+### Now enter a year less than 2010
+```
+curl --location --request POST 'https://t9nde3gpp2.execute-api.us-east-1.amazonaws.com/Prod/123?order=ORD12345' \
+--header 'Content-Type: application/json' \
+--header 'custom-agent: MyMobileApp/1.0' \
+--data-raw '{
+ "make":"MINI",
+ "model":"Countryman",
+ "year": 2002
+}'
+```
+Expected response: `{"message": "Invalid request body"}`
+
+Try some other combinations and see what you get!
+
+## Cleanup
+
+1. Change directory to the pattern directory:
+ ```
+ cd apigw-data-validation-tf
+ ```
+1. Delete all created resources by Terraform
+ ```bash
+ terraform destroy
+ ```
+1. Confirm all created resources has been deleted
+ ```bash
+ terraform show
+ ```
+
+----
+Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+SPDX-License-Identifier: MIT-0
diff --git a/apigw-data-validation-tf/apigw-data-validation-tf.json b/apigw-data-validation-tf/apigw-data-validation-tf.json
new file mode 100644
index 000000000..913478384
--- /dev/null
+++ b/apigw-data-validation-tf/apigw-data-validation-tf.json
@@ -0,0 +1,76 @@
+{
+ "title": "API Gateway data validation",
+ "description": "Creates an API Gateway with request validation, rejecting invalid requests before Lambda invocation using model schema validation.",
+ "language": "YAML",
+ "level": "300",
+ "framework": "Terraform",
+ "introBox": {
+ "headline": "How it works",
+ "text": [
+ "The data model is declared in the API Gateway resource. The Lambda function then requires the request body to be validated against this model."
+ ]
+ },
+ "gitHub": {
+ "template": {
+ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-data-validation-tf",
+ "templateURL": "serverless-patterns/apigw-data-validation-tf",
+ "projectFolder": "apigw-data-validation-tf",
+ "templateFile": "main.tf"
+ }
+ },
+ "resources": {
+ "bullets": [
+ {
+ "text": "API Gateway model example",
+ "link": "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html#sam-api-models"
+ },
+ {
+ "text": "JSON Schema",
+ "link": "https://datatracker.ietf.org/doc/html/draft-zyp-json-schema-04#section-4.1"
+ }
+ ]
+ },
+ "deploy": {
+ "text": [
+ "terraform init",
+ "terraform apply"
+ ]
+ },
+ "testing": {
+ "text": [
+ "See the Github repo for detailed testing instructions."
+ ]
+ },
+ "cleanup": {
+ "text": [
+ "terraform destroy",
+ "terraform show"
+ ]
+ },
+ "authors": [
+ {
+ "name": "Makendran G",
+ "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing",
+ "bio": "Cloud Support Engineer @ AWS",
+ "linkedin": "makendran",
+ "twitter": "@MakendranG"
+ }
+ ],
+ "patternArch": {
+ "icon1": {
+ "x": 20,
+ "y": 50,
+ "service": "internet"
+ },
+ "icon2": {
+ "x": 80,
+ "y": 50,
+ "service": "apigw",
+ "label": "API Gateway REST API"
+ },
+ "line1": {
+ "from": "icon1",
+ "to": "icon2"
+ }
+ }
+}
diff --git a/apigw-data-validation-tf/example-pattern.json b/apigw-data-validation-tf/example-pattern.json
new file mode 100644
index 000000000..3fcb18d63
--- /dev/null
+++ b/apigw-data-validation-tf/example-pattern.json
@@ -0,0 +1,59 @@
+{
+ "title": "API Gateway data validation",
+ "description": "Creates an API Gateway with request validation, rejecting invalid requests before Lambda invocation using model schema validation.",
+ "language": "YAML",
+ "level": "300",
+ "framework": "Terraform",
+ "introBox": {
+ "headline": "How it works",
+ "text": [
+ "The data model is declared in the API Gateway resource. The Lambda function then requires the request body to be validated against this model."
+ ]
+ },
+ "gitHub": {
+ "template": {
+ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-data-validation-tf",
+ "templateURL": "serverless-patterns/apigw-data-validation-tf",
+ "projectFolder": "apigw-data-validation-tf",
+ "templateFile": "main.tf"
+ }
+ },
+ "resources": {
+ "bullets": [
+ {
+ "text": "API Gateway model example",
+ "link": "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html#sam-api-models"
+ },
+ {
+ "text": "JSON Schema",
+ "link": "https://datatracker.ietf.org/doc/html/draft-zyp-json-schema-04#section-4.1"
+ }
+ ]
+ },
+ "deploy": {
+ "text": [
+ "terraform init",
+ "terraform apply"
+ ]
+ },
+ "testing": {
+ "text": [
+ "See the Github repo for detailed testing instructions."
+ ]
+ },
+ "cleanup": {
+ "text": [
+ "terraform destroy",
+ "terraform show"
+ ]
+ },
+ "authors": [
+ {
+ "name": "Makendran G",
+ "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing",
+ "bio": "Cloud Support Engineer @ AWS",
+ "linkedin": "makendran",
+ "twitter": "@MakendranG"
+ }
+ ]
+}
diff --git a/apigw-data-validation-tf/main.tf b/apigw-data-validation-tf/main.tf
new file mode 100644
index 000000000..1225ff756
--- /dev/null
+++ b/apigw-data-validation-tf/main.tf
@@ -0,0 +1,206 @@
+# Provider configuration
+provider "aws" {
+ region = "us-east-1" # Change this to your desired region
+}
+
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 4.0"
+ }
+ archive = {
+ source = "hashicorp/archive"
+ version = "~> 2.0"
+ }
+ null = {
+ source = "hashicorp/null"
+ version = "~> 3.0"
+ }
+ }
+}
+
+# Archive the Lambda function code
+data "archive_file" "lambda_zip" {
+ type = "zip"
+ source_dir = "${path.module}/src"
+ output_path = "${path.module}/lambda.zip"
+}
+
+# API Gateway REST API
+resource "aws_api_gateway_rest_api" "main_api" {
+ name = "validation-api"
+ description = "API Gateway with data validation"
+
+ body = jsonencode({
+ openapi = "3.0.1"
+ info = {
+ title = "validation-api"
+ version = "1.0"
+ }
+ components = {
+ schemas = {
+ Vehicle = {
+ type = "object"
+ required = ["make", "model", "year"]
+ properties = {
+ make = {
+ type = "string"
+ }
+ model = {
+ type = "string"
+ }
+ year = {
+ type = "integer"
+ minimum = 2010
+ }
+ color = {
+ type = "string"
+ enum = ["green", "red", "blue"]
+ }
+ }
+ }
+ }
+ }
+ })
+}
+
+# Lambda Function
+resource "aws_lambda_function" "process_function" {
+ filename = data.archive_file.lambda_zip.output_path
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
+ function_name = "process-function"
+ role = aws_iam_role.lambda_role.arn
+ handler = "app.lambda_handler"
+ runtime = "python3.13"
+ architectures = ["arm64"]
+ timeout = 3
+
+ depends_on = [
+ data.archive_file.lambda_zip
+ ]
+}
+
+# IAM Role for Lambda
+resource "aws_iam_role" "lambda_role" {
+ name = "process_function_role"
+
+ assume_role_policy = jsonencode({
+ Version = "2012-10-17"
+ Statement = [
+ {
+ Action = "sts:AssumeRole"
+ Effect = "Allow"
+ Principal = {
+ Service = "lambda.amazonaws.com"
+ }
+ }
+ ]
+ })
+}
+
+# Basic Lambda execution policy
+resource "aws_iam_role_policy_attachment" "lambda_basic" {
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+ role = aws_iam_role.lambda_role.name
+}
+
+# API Gateway Resource
+resource "aws_api_gateway_resource" "api_resource" {
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ parent_id = aws_api_gateway_rest_api.main_api.root_resource_id
+ path_part = "{id}"
+}
+
+# API Gateway Method
+resource "aws_api_gateway_method" "api_method" {
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ resource_id = aws_api_gateway_resource.api_resource.id
+ http_method = "POST"
+ authorization = "NONE"
+
+ request_parameters = {
+ "method.request.querystring.order" = true
+ "method.request.header.custom-agent" = true
+ }
+
+ request_validator_id = aws_api_gateway_request_validator.validator.id
+ request_models = {
+ "application/json" = aws_api_gateway_model.vehicle_model.name
+ }
+}
+
+# Request Validator
+resource "aws_api_gateway_request_validator" "validator" {
+ name = "validator"
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ validate_request_body = true
+ validate_request_parameters = true
+}
+
+# Vehicle Model
+resource "aws_api_gateway_model" "vehicle_model" {
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ name = "Vehicle"
+ description = "Vehicle model for validation"
+ content_type = "application/json"
+
+ schema = jsonencode({
+ type = "object"
+ required = ["make", "model", "year"]
+ properties = {
+ make = {
+ type = "string"
+ }
+ model = {
+ type = "string"
+ }
+ year = {
+ type = "integer"
+ minimum = 2010
+ }
+ color = {
+ type = "string"
+ enum = ["green", "red", "blue"]
+ }
+ }
+ })
+}
+
+# Lambda Integration
+resource "aws_api_gateway_integration" "lambda_integration" {
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ resource_id = aws_api_gateway_resource.api_resource.id
+ http_method = aws_api_gateway_method.api_method.http_method
+ type = "AWS_PROXY"
+ integration_http_method = "POST"
+ uri = aws_lambda_function.process_function.invoke_arn
+}
+
+# Lambda Permission
+resource "aws_lambda_permission" "api_gateway" {
+ statement_id = "AllowAPIGatewayInvoke"
+ action = "lambda:InvokeFunction"
+ function_name = aws_lambda_function.process_function.function_name
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_api_gateway_rest_api.main_api.execution_arn}/*/*/*"
+}
+
+# API Gateway Deployment
+resource "aws_api_gateway_deployment" "api_deployment" {
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ depends_on = [aws_api_gateway_integration.lambda_integration]
+}
+
+# API Gateway Stage
+resource "aws_api_gateway_stage" "prod" {
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
+ rest_api_id = aws_api_gateway_rest_api.main_api.id
+ stage_name = "Prod"
+}
+
+# Output
+output "api_endpoint" {
+ description = "API Gateway endpoint URL for Prod stage"
+ value = "${aws_api_gateway_stage.prod.invoke_url}"
+}
diff --git a/apigw-data-validation-tf/src/app.py b/apigw-data-validation-tf/src/app.py
new file mode 100644
index 000000000..ab542c439
--- /dev/null
+++ b/apigw-data-validation-tf/src/app.py
@@ -0,0 +1,9 @@
+import json
+def lambda_handler(event, context):
+ return {
+ "statusCode": 200,
+ "body": json.dumps({
+ "message": "Data validation succeded",
+ "data": json.loads(event["body"])
+ }),
+ }
\ No newline at end of file