diff --git a/infrastructure/terraform/components/api/README.md b/infrastructure/terraform/components/api/README.md
index 00196a0b..baec57b9 100644
--- a/infrastructure/terraform/components/api/README.md
+++ b/infrastructure/terraform/components/api/README.md
@@ -78,7 +78,9 @@ No requirements.
| [sqs\_alarms](#module\_sqs\_alarms) | ../../modules/alarms-sqs | n/a |
| [sqs\_letter\_updates](#module\_sqs\_letter\_updates) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-sqs.zip | n/a |
| [sqs\_supplier\_allocator](#module\_sqs\_supplier\_allocator) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-sqs.zip | n/a |
+| [sqs\_supplier\_config](#module\_sqs\_supplier\_config) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-sqs.zip | n/a |
| [supplier\_allocator](#module\_supplier\_allocator) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
+| [supplier\_config\_ingress](#module\_supplier\_config\_ingress) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
| [supplier\_ssl](#module\_supplier\_ssl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-ssl.zip | n/a |
| [update\_letter\_queue](#module\_update\_letter\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
| [upsert\_letter](#module\_upsert\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
diff --git a/infrastructure/terraform/components/api/lambda_event_source_mapping_supplier_config_ingress.tf b/infrastructure/terraform/components/api/lambda_event_source_mapping_supplier_config_ingress.tf
new file mode 100644
index 00000000..891e9012
--- /dev/null
+++ b/infrastructure/terraform/components/api/lambda_event_source_mapping_supplier_config_ingress.tf
@@ -0,0 +1,9 @@
+resource "aws_lambda_event_source_mapping" "supplier_config_ingress" {
+ event_source_arn = module.sqs_supplier_config.sqs_queue_arn
+ function_name = module.supplier_config_ingress.function_name
+ batch_size = 10
+ maximum_batching_window_in_seconds = 5
+ function_response_types = [
+ "ReportBatchItemFailures"
+ ]
+}
diff --git a/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf
new file mode 100644
index 00000000..b11740b8
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_lambda_supplier_config_ingress.tf
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+module "supplier_config_ingress" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip"
+
+ function_name = "supplier-config-ingress"
+ description = "Persist supplier config changes"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ group = var.group
+
+ log_retention_in_days = var.log_retention_in_days
+ kms_key_arn = module.kms.key_arn
+
+ iam_policy_document = {
+ body = data.aws_iam_policy_document.supplier_config_ingress_lambda.json
+ }
+
+ function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
+ function_code_base_path = local.aws_lambda_functions_dir_path
+ function_code_dir = "supplier-config-ingress/dist"
+ function_include_common = true
+ handler_function_name = "supplierConfigHandler"
+ runtime = "nodejs22.x"
+ memory = 512
+ timeout = 29
+ log_level = var.log_level
+
+ force_lambda_code_deploy = var.force_lambda_code_deploy
+ enable_lambda_insights = false
+
+ log_destination_arn = local.destination_arn
+ log_subscription_role_arn = local.acct.log_subscription_role_arn
+
+ lambda_env_vars = merge(local.common_lambda_env_vars, {})
+}
+
+data "aws_iam_policy_document" "supplier_config_ingress_lambda" {
+ statement {
+ sid = "KMSPermissions"
+ effect = "Allow"
+
+ actions = [
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ ]
+
+ resources = [
+ module.kms.key_arn,
+ ]
+ }
+
+ statement {
+ sid = "AllowSQSRead"
+ effect = "Allow"
+
+ actions = [
+ "sqs:ReceiveMessage",
+ "sqs:DeleteMessage",
+ "sqs:GetQueueAttributes"
+ ]
+
+ resources = [
+ module.sqs_supplier_config.sqs_queue_arn
+ ]
+ }
+}
diff --git a/infrastructure/terraform/components/api/module_sqs_supplier_config.tf b/infrastructure/terraform/components/api/module_sqs_supplier_config.tf
new file mode 100644
index 00000000..e3c9d51f
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_sqs_supplier_config.tf
@@ -0,0 +1,48 @@
+module "sqs_supplier_config" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-sqs.zip"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ name = "supplier-config"
+
+ sqs_kms_key_arn = module.kms.key_arn
+
+ visibility_timeout_seconds = 60
+
+ create_dlq = true
+ sqs_policy_overload = data.aws_iam_policy_document.supplier_config_queue_policy.json
+}
+
+data "aws_iam_policy_document" "supplier_config_queue_policy" {
+ version = "2012-10-17"
+
+ statement {
+ sid = "AllowSNSPermissions"
+ effect = "Allow"
+
+ principals {
+ type = "Service"
+ identifiers = ["sns.amazonaws.com"]
+ }
+
+ actions = [
+ "sqs:SendMessage",
+ "sqs:ListQueueTags",
+ "sqs:GetQueueUrl",
+ "sqs:GetQueueAttributes",
+ ]
+
+ resources = [
+ "arn:aws:sqs:${var.region}:${var.aws_account_id}:${var.project}-${var.environment}-${var.component}-supplier-config-queue"
+ ]
+
+ condition {
+ test = "ArnEquals"
+ variable = "aws:SourceArn"
+ values = [module.eventsub.sns_topic.arn]
+ }
+ }
+}
diff --git a/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_supplier_config.tf b/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_supplier_config.tf
new file mode 100644
index 00000000..306cb4f2
--- /dev/null
+++ b/infrastructure/terraform/components/api/sns_topic_subscription_eventsub_sqs_supplier_config.tf
@@ -0,0 +1,11 @@
+resource "aws_sns_topic_subscription" "eventsub_sqs_supplier_config" {
+ topic_arn = module.eventsub.sns_topic.arn
+ protocol = "sqs"
+ endpoint = module.sqs_supplier_config.sqs_queue_arn
+ raw_message_delivery = true
+
+ filter_policy_scope = "MessageBody"
+ filter_policy = jsonencode({
+ type = [{ prefix = "uk.nhs.notify.supplier-config" }]
+ })
+}
diff --git a/lambdas/supplier-config-ingress/.gitignore b/lambdas/supplier-config-ingress/.gitignore
new file mode 100644
index 00000000..80323f7c
--- /dev/null
+++ b/lambdas/supplier-config-ingress/.gitignore
@@ -0,0 +1,4 @@
+coverage
+node_modules
+dist
+.reports
diff --git a/lambdas/supplier-config-ingress/jest.config.ts b/lambdas/supplier-config-ingress/jest.config.ts
new file mode 100644
index 00000000..2708bc41
--- /dev/null
+++ b/lambdas/supplier-config-ingress/jest.config.ts
@@ -0,0 +1,55 @@
+const baseJestConfig = {
+ preset: "ts-jest",
+ extensionsToTreatAsEsm: [".ts"],
+ transform: {
+ "^.+\\.ts$": [
+ "ts-jest",
+ {
+ useESM: true,
+ },
+ ],
+ },
+
+ // Automatically clear mock calls, instances, contexts and results before every test
+ clearMocks: true,
+
+ // Indicates whether the coverage information should be collected while executing the test
+ collectCoverage: true,
+
+ // The directory where Jest should output its coverage files
+ coverageDirectory: "./.reports/unit/coverage",
+
+ // Indicates which provider should be used to instrument code for coverage
+ coverageProvider: "babel",
+
+ coverageThreshold: {
+ global: {
+ branches: 100,
+ functions: 100,
+ lines: 100,
+ statements: -10,
+ },
+ },
+
+ coveragePathIgnorePatterns: ["/__tests__/"],
+ testPathIgnorePatterns: [".build"],
+ testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
+
+ // Use this configuration option to add custom reporters to Jest
+ reporters: [
+ "default",
+ [
+ "jest-html-reporter",
+ {
+ pageTitle: "Test Report",
+ outputPath: "./.reports/unit/test-report.html",
+ includeFailureMsg: true,
+ },
+ ],
+ ],
+
+ // The test environment that will be used for testing
+ testEnvironment: "node",
+};
+
+export default baseJestConfig;
diff --git a/lambdas/supplier-config-ingress/package.json b/lambdas/supplier-config-ingress/package.json
new file mode 100644
index 00000000..b3ad67bb
--- /dev/null
+++ b/lambdas/supplier-config-ingress/package.json
@@ -0,0 +1,16 @@
+{
+ "dependencies": {
+ "@types/aws-lambda": "^8.10.148",
+ "esbuild": "^0.27.2"
+ },
+ "name": "nhs-notify-supplier-api-supplier-config-ingress",
+ "private": true,
+ "scripts": {
+ "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "test:unit": "jest",
+ "typecheck": "tsc --noEmit"
+ },
+ "version": "0.0.1"
+}
diff --git a/lambdas/supplier-config-ingress/src/__tests__/index.test.ts b/lambdas/supplier-config-ingress/src/__tests__/index.test.ts
new file mode 100644
index 00000000..390ea3dc
--- /dev/null
+++ b/lambdas/supplier-config-ingress/src/__tests__/index.test.ts
@@ -0,0 +1,12 @@
+import type { SQSEvent } from "aws-lambda";
+import { supplierConfigHandler } from "..";
+
+describe("supplierConfigHandler", () => {
+ it("returns an empty batchItemFailures list", async () => {
+ const event = { Records: [] } as unknown as SQSEvent;
+
+ const result = await supplierConfigHandler(event);
+
+ expect(result).toEqual({ batchItemFailures: [] });
+ });
+});
diff --git a/lambdas/supplier-config-ingress/src/index.ts b/lambdas/supplier-config-ingress/src/index.ts
new file mode 100644
index 00000000..70af21c8
--- /dev/null
+++ b/lambdas/supplier-config-ingress/src/index.ts
@@ -0,0 +1,9 @@
+import type { SQSBatchResponse, SQSEvent } from "aws-lambda";
+
+// eslint-disable-next-line import-x/prefer-default-export
+export const supplierConfigHandler = async (
+ _event: SQSEvent,
+): Promise => {
+ // Implementation to be done under CCM-17379
+ return { batchItemFailures: [] };
+};
diff --git a/lambdas/supplier-config-ingress/tsconfig.json b/lambdas/supplier-config-ingress/tsconfig.json
new file mode 100644
index 00000000..528c0c8b
--- /dev/null
+++ b/lambdas/supplier-config-ingress/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "types": [
+ "jest",
+ "node"
+ ]
+ },
+ "extends": "../../tsconfig.base.json",
+ "include": [
+ "src/**/*",
+ "jest.config.ts"
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
index b43a56c1..4c80085c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -281,6 +281,14 @@
"pino": "bin.js"
}
},
+ "lambdas/supplier-config-ingress": {
+ "name": "nhs-notify-supplier-api-supplier-config-ingress",
+ "version": "0.0.1",
+ "dependencies": {
+ "@types/aws-lambda": "^8.10.148",
+ "esbuild": "^0.27.2"
+ }
+ },
"lambdas/update-letter-queue": {
"name": "nhs-notify-supplier-api-update-letter-queue",
"version": "0.0.1",
@@ -17678,6 +17686,10 @@
"resolved": "lambdas/mi-updates-transformer",
"link": true
},
+ "node_modules/nhs-notify-supplier-api-supplier-config-ingress": {
+ "resolved": "lambdas/supplier-config-ingress",
+ "link": true
+ },
"node_modules/nhs-notify-supplier-api-suppliers-data-utility": {
"resolved": "scripts/utilities/supplier-data",
"link": true