diff --git a/.github/workflows/test-on-push-and-pr.yml b/.github/workflows/test-on-push-and-pr.yml
index f12bf0e..5b80d23 100644
--- a/.github/workflows/test-on-push-and-pr.yml
+++ b/.github/workflows/test-on-push-and-pr.yml
@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Run 'pr' target
       run: make pr
 
@@ -19,7 +19,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Run alpine integration tests
       run: DISTRO=alpine make test-integ
 
@@ -27,23 +27,15 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Run amazonlinux integration tests
       run: DISTRO=amazonlinux make test-integ
 
-  centos:
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Run centos integration tests
-      run: DISTRO=centos make test-integ
-
   debian:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Run debian integration tests
       run: DISTRO=debian make test-integ
 
@@ -51,6 +43,6 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Run ubuntu integration tests
       run: DISTRO=ubuntu make test-integ
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bd5c9b7..9d46e4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -148,3 +148,8 @@ cython_debug/
 
 # Test files generated
 tmp*.py
+
+# dependencies
+deps/artifacts/
+deps/aws-lambda-cpp-*/
+deps/curl-*/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index dcabbf4..bb66b03 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,5 +3,5 @@ repos:
     rev: 19.3b0
     hooks:
       - id: black
-        language_version: python3.6
+        language_version: python3.9
         exclude_types: ['markdown', 'ini', 'toml', 'rst']
diff --git a/Makefile b/Makefile
index ff99587..521b61c 100644
--- a/Makefile
+++ b/Makefile
@@ -41,9 +41,10 @@ dev: init test
 
 # Verifications to run before sending a pull request
 .PHONY: pr
-pr: init check-format check-security dev setup-codebuild-agent
-	CODEBUILD_IMAGE_TAG=codebuild-agent DISTRO="$(DISTRO)" tests/integration/codebuild-local/test_all.sh tests/integration/codebuild
+pr: init check-format check-security dev
 
+codebuild: setup-codebuild-agent
+	CODEBUILD_IMAGE_TAG=codebuild-agent DISTRO="$(DISTRO)" tests/integration/codebuild-local/test_all.sh tests/integration/codebuild
 
 .PHONY: clean
 clean:
diff --git a/README.md b/README.md
index 248fde4..4a96a3f 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,12 @@ We have open-sourced a set of software packages, Runtime Interface Clients (RIC)
   base images to be Lambda compatible.
 The Lambda Runtime Interface Client is a lightweight interface that allows your runtime to receive requests from and send requests to the Lambda service.
 
-The Lambda Python Runtime Interface Client is vended through [pip](https://pypi.org/project/awslambdaric). 
+The Lambda Python Runtime Interface Client is vended through [pip](https://pypi.org/project/awslambdaric).
 You can include this package in your preferred base image to make that base image Lambda compatible.
 
 ## Requirements
 The Python Runtime Interface Client package currently supports Python versions:
- - 3.7.x up to and including 3.12.x
+ - 3.9.x up to and including 3.13.x
 
 ## Usage
 
@@ -19,7 +19,6 @@ First step is to choose the base image to be used. The supported Linux OS distri
 
  - Amazon Linux 2
  - Alpine
- - CentOS
  - Debian
  - Ubuntu
 
@@ -104,18 +103,18 @@ def handler(event, context):
 
 ### Local Testing
 
-To make it easy to locally test Lambda functions packaged as container images we open-sourced a lightweight web-server, Lambda Runtime Interface Emulator (RIE), which allows your function packaged as a container image to accept HTTP requests. You can install the [AWS Lambda Runtime Interface Emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator) on your local machine to test your function. Then when you run the image function, you set the entrypoint to be the emulator. 
+To make it easy to locally test Lambda functions packaged as container images we open-sourced a lightweight web-server, Lambda Runtime Interface Emulator (RIE), which allows your function packaged as a container image to accept HTTP requests. You can install the [AWS Lambda Runtime Interface Emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator) on your local machine to test your function. Then when you run the image function, you set the entrypoint to be the emulator.
 
 *To install the emulator and test your Lambda function*
 
-1) From your project directory, run the following command to download the RIE from GitHub and install it on your local machine. 
+1) From your project directory, run the following command to download the RIE from GitHub and install it on your local machine.
 
 ```shell script
 mkdir -p ~/.aws-lambda-rie && \
     curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
     chmod +x ~/.aws-lambda-rie/aws-lambda-rie
 ```
-2) Run your Lambda image function using the docker run command. 
+2) Run your Lambda image function using the docker run command.
 
 ```shell script
 docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
@@ -124,9 +123,9 @@ docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
         /usr/local/bin/python -m awslambdaric app.handler
 ```
 
-This runs the image as a container and starts up an endpoint locally at `http://localhost:9000/2015-03-31/functions/function/invocations`. 
+This runs the image as a container and starts up an endpoint locally at `http://localhost:9000/2015-03-31/functions/function/invocations`.
 
-3) Post an event to the following endpoint using a curl command: 
+3) Post an event to the following endpoint using a curl command:
 
 ```shell script
 curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
@@ -175,4 +174,4 @@ If you discover a potential security issue in this project we ask that you notif
 
 ## License
 
-This project is licensed under the Apache-2.0 License.
\ No newline at end of file
+This project is licensed under the Apache-2.0 License.
diff --git a/RELEASE.CHANGELOG.md b/RELEASE.CHANGELOG.md
index 4f71031..fc45791 100644
--- a/RELEASE.CHANGELOG.md
+++ b/RELEASE.CHANGELOG.md
@@ -1,3 +1,34 @@
+### May 26, 2025
+`3.1.1`
+- Move unhandled exception warning message to init errors. ([#189](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/189))
+
+### May 21, 2025
+`3.1.0`
+- Add support for multi tenancy ([#187](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/187))
+
+### February 27, 2024
+`3.0.2`
+- Update `simplejson` to `3.20.1`([#184](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/184))
+
+### January 27, 2024
+`3.0.1`
+- Don't enforce text format on uncaught exception warning message ([#182](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/182))
+
+### November 19, 2024
+`3.0.0`
+- Drop support for deprecated python versions ([#179](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/179))
+- Add support for snapstart runtime hooks ([#176](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/176))
+
+### August 23, 2024
+`2.2.1`:
+- Patch libcurl configure.ac to work with later versions of autoconf ([#166](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/168))
+
+### August 8, 2024
+
+`2.2.0`:
+
+- Propogate error type in header when reporting init error to RAPID ([#166](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/166))
+
 ### July 31, 2024
 
 `2.1.0`:
diff --git a/awslambdaric/__init__.py b/awslambdaric/__init__.py
index 9d397ae..5605903 100644
--- a/awslambdaric/__init__.py
+++ b/awslambdaric/__init__.py
@@ -2,4 +2,4 @@
 Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 """
 
-__version__ = "2.1.0"
+__version__ = "3.1.1"
diff --git a/awslambdaric/bootstrap.py b/awslambdaric/bootstrap.py
index 60aa216..cb8d5c3 100644
--- a/awslambdaric/bootstrap.py
+++ b/awslambdaric/bootstrap.py
@@ -31,6 +31,8 @@
 _AWS_LAMBDA_LOG_LEVEL = _get_log_level_from_env_var(
     os.environ.get("AWS_LAMBDA_LOG_LEVEL")
 )
+AWS_LAMBDA_INITIALIZATION_TYPE = "AWS_LAMBDA_INITIALIZATION_TYPE"
+INIT_TYPE_SNAP_START = "snap-start"
 
 
 def _get_handler(handler):
@@ -100,7 +102,6 @@ def replace_line_indentation(line, indent_char, new_indent_char):
 
 if _AWS_LAMBDA_LOG_FORMAT == LogFormat.JSON:
     _ERROR_FRAME_TYPE = _JSON_FRAME_TYPES[logging.ERROR]
-    _WARNING_FRAME_TYPE = _JSON_FRAME_TYPES[logging.WARNING]
 
     def log_error(error_result, log_sink):
         error_result = {
@@ -116,7 +117,6 @@ def log_error(error_result, log_sink):
 
 else:
     _ERROR_FRAME_TYPE = _TEXT_FRAME_TYPES[logging.ERROR]
-    _WARNING_FRAME_TYPE = _TEXT_FRAME_TYPES[logging.WARNING]
 
     def log_error(error_result, log_sink):
         error_description = "[ERROR]"
@@ -158,6 +158,7 @@ def handle_event_request(
     cognito_identity_json,
     invoked_function_arn,
     epoch_deadline_time_in_ms,
+    tenant_id,
     log_sink,
 ):
     error_result = None
@@ -168,6 +169,7 @@ def handle_event_request(
             epoch_deadline_time_in_ms,
             invoke_id,
             invoked_function_arn,
+            tenant_id,
         )
         event = lambda_runtime_client.marshaller.unmarshal_request(
             event_body, content_type
@@ -199,9 +201,7 @@ def handle_event_request(
         )
 
     if error_result is not None:
-        from .lambda_literals import lambda_unhandled_exception_warning_message
 
-        log_sink.log(lambda_unhandled_exception_warning_message, _WARNING_FRAME_TYPE)
         log_error(error_result, log_sink)
         lambda_runtime_client.post_invocation_error(
             invoke_id, to_json(error_result), to_json(xray_fault)
@@ -229,6 +229,7 @@ def create_lambda_context(
     epoch_deadline_time_in_ms,
     invoke_id,
     invoked_function_arn,
+    tenant_id,
 ):
     client_context = None
     if client_context_json:
@@ -243,6 +244,7 @@ def create_lambda_context(
         cognito_identity,
         epoch_deadline_time_in_ms,
         invoked_function_arn,
+        tenant_id,
     )
 
 
@@ -286,6 +288,29 @@ def extract_traceback(tb):
     ]
 
 
+def on_init_complete(lambda_runtime_client, log_sink):
+    from . import lambda_runtime_hooks_runner
+
+    try:
+        lambda_runtime_hooks_runner.run_before_snapshot()
+        lambda_runtime_client.restore_next()
+    except:
+        error_result = build_fault_result(sys.exc_info(), None)
+        log_error(error_result, log_sink)
+        lambda_runtime_client.post_init_error(
+            error_result, FaultException.BEFORE_SNAPSHOT_ERROR
+        )
+        sys.exit(64)
+
+    try:
+        lambda_runtime_hooks_runner.run_after_restore()
+    except:
+        error_result = build_fault_result(sys.exc_info(), None)
+        log_error(error_result, log_sink)
+        lambda_runtime_client.report_restore_error(error_result)
+        sys.exit(65)
+
+
 class LambdaLoggerHandler(logging.Handler):
     def __init__(self, log_sink):
         logging.Handler.__init__(self)
@@ -314,6 +339,7 @@ def emit(self, record):
 class LambdaLoggerFilter(logging.Filter):
     def filter(self, record):
         record.aws_request_id = _GLOBAL_AWS_REQUEST_ID or ""
+        record.tenant_id = _GLOBAL_TENANT_ID
         return True
 
 
@@ -422,6 +448,7 @@ def create_log_sink():
 
 
 _GLOBAL_AWS_REQUEST_ID = None
+_GLOBAL_TENANT_ID = None
 
 
 def _setup_logging(log_format, log_level, log_sink):
@@ -454,9 +481,10 @@ def run(app_root, handler, lambda_runtime_api_addr):
     sys.stdout = Unbuffered(sys.stdout)
     sys.stderr = Unbuffered(sys.stderr)
 
-    use_thread_for_polling_next = (
-        os.environ.get("AWS_EXECUTION_ENV") == "AWS_Lambda_python3.12"
-    )
+    use_thread_for_polling_next = os.environ.get("AWS_EXECUTION_ENV") in {
+        "AWS_Lambda_python3.12",
+        "AWS_Lambda_python3.13",
+    }
 
     with create_log_sink() as log_sink:
         lambda_runtime_client = LambdaRuntimeClient(
@@ -466,7 +494,7 @@ def run(app_root, handler, lambda_runtime_api_addr):
 
         try:
             _setup_logging(_AWS_LAMBDA_LOG_FORMAT, _AWS_LAMBDA_LOG_LEVEL, log_sink)
-            global _GLOBAL_AWS_REQUEST_ID
+            global _GLOBAL_AWS_REQUEST_ID, _GLOBAL_TENANT_ID
 
             request_handler = _get_handler(handler)
         except FaultException as e:
@@ -479,15 +507,22 @@ def run(app_root, handler, lambda_runtime_api_addr):
             error_result = build_fault_result(sys.exc_info(), None)
 
         if error_result is not None:
+            from .lambda_literals import lambda_unhandled_exception_warning_message
+
+            logging.warning(lambda_unhandled_exception_warning_message)
             log_error(error_result, log_sink)
-            lambda_runtime_client.post_init_error(to_json(error_result))
+            lambda_runtime_client.post_init_error(error_result)
 
             sys.exit(1)
 
+        if os.environ.get(AWS_LAMBDA_INITIALIZATION_TYPE) == INIT_TYPE_SNAP_START:
+            on_init_complete(lambda_runtime_client, log_sink)
+
         while True:
             event_request = lambda_runtime_client.wait_next_invocation()
 
             _GLOBAL_AWS_REQUEST_ID = event_request.invoke_id
+            _GLOBAL_TENANT_ID = event_request.tenant_id
 
             update_xray_env_variable(event_request.x_amzn_trace_id)
 
@@ -501,5 +536,6 @@ def run(app_root, handler, lambda_runtime_api_addr):
                 event_request.cognito_identity,
                 event_request.invoked_function_arn,
                 event_request.deadline_time_in_ms,
+                event_request.tenant_id,
                 log_sink,
             )
diff --git a/awslambdaric/lambda_context.py b/awslambdaric/lambda_context.py
index 1465827..e0a3363 100644
--- a/awslambdaric/lambda_context.py
+++ b/awslambdaric/lambda_context.py
@@ -16,6 +16,7 @@ def __init__(
         cognito_identity,
         epoch_deadline_time_in_ms,
         invoked_function_arn=None,
+        tenant_id=None,
     ):
         self.aws_request_id = invoke_id
         self.log_group_name = os.environ.get("AWS_LAMBDA_LOG_GROUP_NAME")
@@ -24,6 +25,7 @@ def __init__(
         self.memory_limit_in_mb = os.environ.get("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")
         self.function_version = os.environ.get("AWS_LAMBDA_FUNCTION_VERSION")
         self.invoked_function_arn = invoked_function_arn
+        self.tenant_id = tenant_id
 
         self.client_context = make_obj_from_dict(ClientContext, client_context)
         if self.client_context is not None:
@@ -65,7 +67,8 @@ def __repr__(self):
             f"function_version={self.function_version},"
             f"invoked_function_arn={self.invoked_function_arn},"
             f"client_context={self.client_context},"
-            f"identity={self.identity}"
+            f"identity={self.identity},"
+            f"tenant_id={self.tenant_id}"
             "])"
         )
 
diff --git a/awslambdaric/lambda_runtime_client.py b/awslambdaric/lambda_runtime_client.py
index 07243fc..ba4ad92 100644
--- a/awslambdaric/lambda_runtime_client.py
+++ b/awslambdaric/lambda_runtime_client.py
@@ -5,6 +5,9 @@
 import sys
 from awslambdaric import __version__
 from .lambda_runtime_exception import FaultException
+from .lambda_runtime_marshaller import to_json
+
+ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type"
 
 
 def _user_agent():
@@ -59,22 +62,57 @@ def __init__(self, lambda_runtime_address, use_thread_for_polling_next=False):
             # Not defining symbol as global to avoid relying on TPE being imported unconditionally.
             self.ThreadPoolExecutor = ThreadPoolExecutor
 
-    def post_init_error(self, error_response_data):
+    def call_rapid(
+        self, http_method, endpoint, expected_http_code, payload=None, headers=None
+    ):
         # These imports are heavy-weight. They implicitly trigger `import ssl, hashlib`.
         # Importing them lazily to speed up critical path of a common case.
-        import http
         import http.client
 
         runtime_connection = http.client.HTTPConnection(self.lambda_runtime_address)
         runtime_connection.connect()
-        endpoint = "/2018-06-01/runtime/init/error"
-        runtime_connection.request("POST", endpoint, error_response_data)
+        if http_method == "GET":
+            runtime_connection.request(http_method, endpoint)
+        else:
+            runtime_connection.request(
+                http_method, endpoint, to_json(payload), headers=headers
+            )
+
         response = runtime_connection.getresponse()
         response_body = response.read()
-
-        if response.code != http.HTTPStatus.ACCEPTED:
+        if response.code != expected_http_code:
             raise LambdaRuntimeClientError(endpoint, response.code, response_body)
 
+    def post_init_error(self, error_response_data, error_type_override=None):
+        import http
+
+        endpoint = "/2018-06-01/runtime/init/error"
+        headers = {
+            ERROR_TYPE_HEADER: (
+                error_type_override
+                if error_type_override
+                else error_response_data["errorType"]
+            )
+        }
+        self.call_rapid(
+            "POST", endpoint, http.HTTPStatus.ACCEPTED, error_response_data, headers
+        )
+
+    def restore_next(self):
+        import http
+
+        endpoint = "/2018-06-01/runtime/restore/next"
+        self.call_rapid("GET", endpoint, http.HTTPStatus.OK)
+
+    def report_restore_error(self, restore_error_data):
+        import http
+
+        endpoint = "/2018-06-01/runtime/restore/error"
+        headers = {ERROR_TYPE_HEADER: FaultException.AFTER_RESTORE_ERROR}
+        self.call_rapid(
+            "POST", endpoint, http.HTTPStatus.ACCEPTED, restore_error_data, headers
+        )
+
     def wait_next_invocation(self):
         # Calling runtime_client.next() from a separate thread unblocks the main thread,
         # which can then process signals.
@@ -99,6 +137,7 @@ def wait_next_invocation(self):
             deadline_time_in_ms=headers.get("Lambda-Runtime-Deadline-Ms"),
             client_context=headers.get("Lambda-Runtime-Client-Context"),
             cognito_identity=headers.get("Lambda-Runtime-Cognito-Identity"),
+            tenant_id=headers.get("Lambda-Runtime-Aws-Tenant-Id"),
             content_type=headers.get("Content-Type"),
             event_body=response_body,
         )
diff --git a/awslambdaric/lambda_runtime_exception.py b/awslambdaric/lambda_runtime_exception.py
index e09af70..3ea5b29 100644
--- a/awslambdaric/lambda_runtime_exception.py
+++ b/awslambdaric/lambda_runtime_exception.py
@@ -11,6 +11,8 @@ class FaultException(Exception):
     IMPORT_MODULE_ERROR = "Runtime.ImportModuleError"
     BUILT_IN_MODULE_CONFLICT = "Runtime.BuiltInModuleConflict"
     MALFORMED_HANDLER_NAME = "Runtime.MalformedHandlerName"
+    BEFORE_SNAPSHOT_ERROR = "Runtime.BeforeSnapshotError"
+    AFTER_RESTORE_ERROR = "Runtime.AfterRestoreError"
     LAMBDA_CONTEXT_UNMARSHAL_ERROR = "Runtime.LambdaContextUnmarshalError"
     LAMBDA_RUNTIME_CLIENT_ERROR = "Runtime.LambdaRuntimeClientError"
 
diff --git a/awslambdaric/lambda_runtime_hooks_runner.py b/awslambdaric/lambda_runtime_hooks_runner.py
new file mode 100644
index 0000000..8aee181
--- /dev/null
+++ b/awslambdaric/lambda_runtime_hooks_runner.py
@@ -0,0 +1,18 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+from snapshot_restore_py import get_before_snapshot, get_after_restore
+
+
+def run_before_snapshot():
+    before_snapshot_callables = get_before_snapshot()
+    while before_snapshot_callables:
+        # Using pop as before checkpoint callables are executed in the reverse order of their registration
+        func, args, kwargs = before_snapshot_callables.pop()
+        func(*args, **kwargs)
+
+
+def run_after_restore():
+    after_restore_callables = get_after_restore()
+    for func, args, kwargs in after_restore_callables:
+        func(*args, **kwargs)
diff --git a/awslambdaric/lambda_runtime_log_utils.py b/awslambdaric/lambda_runtime_log_utils.py
index 7ed9940..9ddbcfb 100644
--- a/awslambdaric/lambda_runtime_log_utils.py
+++ b/awslambdaric/lambda_runtime_log_utils.py
@@ -30,6 +30,7 @@
     "processName",
     "process",
     "aws_request_id",
+    "tenant_id",
     "_frame_type",
 }
 
@@ -124,6 +125,9 @@ def format(self, record: logging.LogRecord) -> str:
             "requestId": getattr(record, "aws_request_id", None),
             "location": self.__format_location(record),
         }
+        if hasattr(record, "tenant_id") and record.tenant_id is not None:
+            result["tenantId"] = record.tenant_id
+
         result.update(
             (key, value)
             for key, value in record.__dict__.items()
diff --git a/awslambdaric/lambda_runtime_marshaller.py b/awslambdaric/lambda_runtime_marshaller.py
index 3b28313..4256066 100644
--- a/awslambdaric/lambda_runtime_marshaller.py
+++ b/awslambdaric/lambda_runtime_marshaller.py
@@ -15,7 +15,10 @@
 # We also set 'ensure_ascii=False' so that the encoded json contains unicode characters instead of unicode escape sequences
 class Encoder(json.JSONEncoder):
     def __init__(self):
-        if os.environ.get("AWS_EXECUTION_ENV") == "AWS_Lambda_python3.12":
+        if os.environ.get("AWS_EXECUTION_ENV") in {
+            "AWS_Lambda_python3.12",
+            "AWS_Lambda_python3.13",
+        }:
             super().__init__(use_decimal=False, ensure_ascii=False, allow_nan=True)
         else:
             super().__init__(use_decimal=False, allow_nan=True)
diff --git a/awslambdaric/runtime_client.cpp b/awslambdaric/runtime_client.cpp
index 66252bf..7fb2e95 100644
--- a/awslambdaric/runtime_client.cpp
+++ b/awslambdaric/runtime_client.cpp
@@ -52,9 +52,10 @@ static PyObject *method_next(PyObject *self) {
     auto client_context = response.client_context.c_str();
     auto content_type = response.content_type.c_str();
     auto cognito_id = response.cognito_identity.c_str();
+    auto tenant_id = response.tenant_id.c_str();
 
     PyObject *payload_bytes = PyBytes_FromStringAndSize(payload.c_str(), payload.length());
-    PyObject *result = Py_BuildValue("(O,{s:s,s:s,s:s,s:l,s:s,s:s,s:s})",
+    PyObject *result = Py_BuildValue("(O,{s:s,s:s,s:s,s:l,s:s,s:s,s:s,s:s})",
                          payload_bytes,  //Py_BuildValue() increments reference counter
                          "Lambda-Runtime-Aws-Request-Id", request_id,
                          "Lambda-Runtime-Trace-Id", NULL_IF_EMPTY(trace_id),
@@ -62,7 +63,8 @@ static PyObject *method_next(PyObject *self) {
                          "Lambda-Runtime-Deadline-Ms", deadline,
                          "Lambda-Runtime-Client-Context", NULL_IF_EMPTY(client_context),
                          "Content-Type", NULL_IF_EMPTY(content_type),
-                         "Lambda-Runtime-Cognito-Identity", NULL_IF_EMPTY(cognito_id)
+                         "Lambda-Runtime-Cognito-Identity", NULL_IF_EMPTY(cognito_id),
+                         "Lambda-Runtime-Aws-Tenant-Id", NULL_IF_EMPTY(tenant_id)
     );
 
     Py_XDECREF(payload_bytes);
diff --git a/deps/aws-lambda-cpp-0.2.6.tar.gz b/deps/aws-lambda-cpp-0.2.6.tar.gz
index 26fa498..51d7f51 100644
Binary files a/deps/aws-lambda-cpp-0.2.6.tar.gz and b/deps/aws-lambda-cpp-0.2.6.tar.gz differ
diff --git a/deps/curl-7.83.1.tar.gz b/deps/curl-7.83.1.tar.gz
index b71926a..305bb4f 100644
Binary files a/deps/curl-7.83.1.tar.gz and b/deps/curl-7.83.1.tar.gz differ
diff --git a/deps/patches/aws-lambda-cpp-add-tenant-id.patch b/deps/patches/aws-lambda-cpp-add-tenant-id.patch
new file mode 100644
index 0000000..a7b7172
--- /dev/null
+++ b/deps/patches/aws-lambda-cpp-add-tenant-id.patch
@@ -0,0 +1,39 @@
+diff --git a/include/aws/lambda-runtime/runtime.h b/include/aws/lambda-runtime/runtime.h
+index 7812ff6..96be869 100644
+--- a/include/aws/lambda-runtime/runtime.h
++++ b/include/aws/lambda-runtime/runtime.h
+@@ -61,6 +61,11 @@ struct invocation_request {
+      */
+     std::string content_type;
+ 
++    /**
++     * The Tenant ID of the current invocation.
++     */
++    std::string tenant_id;
++
+     /**
+      * Function execution deadline counted in milliseconds since the Unix epoch.
+      */
+diff --git a/src/runtime.cpp b/src/runtime.cpp
+index e53b2b8..9763282 100644
+--- a/src/runtime.cpp
++++ b/src/runtime.cpp
+@@ -40,6 +40,7 @@ static constexpr auto CLIENT_CONTEXT_HEADER = "lambda-runtime-client-context";
+ static constexpr auto COGNITO_IDENTITY_HEADER = "lambda-runtime-cognito-identity";
+ static constexpr auto DEADLINE_MS_HEADER = "lambda-runtime-deadline-ms";
+ static constexpr auto FUNCTION_ARN_HEADER = "lambda-runtime-invoked-function-arn";
++static constexpr auto TENANT_ID_HEADER = "lambda-runtime-aws-tenant-id";
+ 
+ enum Endpoints {
+     INIT,
+@@ -289,6 +290,10 @@ runtime::next_outcome runtime::get_next()
+         req.function_arn = resp.get_header(FUNCTION_ARN_HEADER);
+     }
+ 
++    if (resp.has_header(TENANT_ID_HEADER)) {
++        req.tenant_id = resp.get_header(TENANT_ID_HEADER);
++    }
++
+     if (resp.has_header(DEADLINE_MS_HEADER)) {
+         auto const& deadline_string = resp.get_header(DEADLINE_MS_HEADER);
+         constexpr int base = 10;
diff --git a/deps/patches/libcurl-configure-template.patch b/deps/patches/libcurl-configure-template.patch
new file mode 100644
index 0000000..e26be47
--- /dev/null
+++ b/deps/patches/libcurl-configure-template.patch
@@ -0,0 +1,131 @@
+diff --git a/configure.ac b/configure.ac
+index d24daea..64aca7f 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -193,87 +193,96 @@ AS_HELP_STRING([--with-schannel],[enable Windows native SSL/TLS]),
+ 
+ OPT_SECURETRANSPORT=no
+ AC_ARG_WITH(secure-transport,dnl
+-AS_HELP_STRING([--with-secure-transport],[enable Apple OS native SSL/TLS]),
++AS_HELP_STRING([--with-secure-transport],[enable Apple OS native SSL/TLS]),[
+   OPT_SECURETRANSPORT=$withval
+   test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }Secure-Transport"
+-)
++])
+ 
+ OPT_AMISSL=no
+ AC_ARG_WITH(amissl,dnl
+-AS_HELP_STRING([--with-amissl],[enable Amiga native SSL/TLS (AmiSSL)]),
++AS_HELP_STRING([--with-amissl],[enable Amiga native SSL/TLS (AmiSSL)]),[
+   OPT_AMISSL=$withval
+-  test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }AmiSSL")
++  test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }AmiSSL"
++])
++
+ 
+ OPT_OPENSSL=no
+ dnl Default to no CA bundle
+ ca="no"
+ AC_ARG_WITH(ssl,dnl
+ AS_HELP_STRING([--with-ssl=PATH],[old version of --with-openssl])
+-AS_HELP_STRING([--without-ssl], [build without any TLS library]),
++AS_HELP_STRING([--without-ssl], [build without any TLS library]),[
+   OPT_SSL=$withval
+   OPT_OPENSSL=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL"
+   fi
++])
+ 
+ AC_ARG_WITH(openssl,dnl
+-AS_HELP_STRING([--with-openssl=PATH],[Where to look for OpenSSL, PATH points to the SSL installation (default: /usr/local/ssl); when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]),
++AS_HELP_STRING([--with-openssl=PATH],[Where to look for OpenSSL, PATH points to the SSL installation (default: /usr/local/ssl); when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]),[
+   OPT_OPENSSL=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL"
+   fi
++])
+ 
+ OPT_GNUTLS=no
+ AC_ARG_WITH(gnutls,dnl
+-AS_HELP_STRING([--with-gnutls=PATH],[where to look for GnuTLS, PATH points to the installation root]),
++AS_HELP_STRING([--with-gnutls=PATH],[where to look for GnuTLS, PATH points to the installation root]),[
+   OPT_GNUTLS=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }GnuTLS")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }GnuTLS"
+   fi
++])
+ 
+ OPT_MBEDTLS=no
+ AC_ARG_WITH(mbedtls,dnl
+-AS_HELP_STRING([--with-mbedtls=PATH],[where to look for mbedTLS, PATH points to the installation root]),
++AS_HELP_STRING([--with-mbedtls=PATH],[where to look for mbedTLS, PATH points to the installation root]),[
+   OPT_MBEDTLS=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }mbedTLS")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }mbedTLS"
+   fi
++])
+ 
+ OPT_WOLFSSL=no
+ AC_ARG_WITH(wolfssl,dnl
+-AS_HELP_STRING([--with-wolfssl=PATH],[where to look for WolfSSL, PATH points to the installation root (default: system lib default)]),
++AS_HELP_STRING([--with-wolfssl=PATH],[where to look for WolfSSL, PATH points to the installation root (default: system lib default)]),[
+   OPT_WOLFSSL=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }wolfSSL")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }wolfSSL"
+   fi
++])
+ 
+ OPT_BEARSSL=no
+ AC_ARG_WITH(bearssl,dnl
+-AS_HELP_STRING([--with-bearssl=PATH],[where to look for BearSSL, PATH points to the installation root]),
++AS_HELP_STRING([--with-bearssl=PATH],[where to look for BearSSL, PATH points to the installation root]),[
+   OPT_BEARSSL=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }BearSSL")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }BearSSL"
+   fi
++])
+ 
+ OPT_RUSTLS=no
+ AC_ARG_WITH(rustls,dnl
+-AS_HELP_STRING([--with-rustls=PATH],[where to look for rustls, PATH points to the installation root]),
++AS_HELP_STRING([--with-rustls=PATH],[where to look for rustls, PATH points to the installation root]),[
+   OPT_RUSTLS=$withval
+   if test X"$withval" != Xno; then
+-    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }rustls")
++    test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }rustls"
+   fi
++])
+ 
+ OPT_NSS_AWARE=no
+ AC_ARG_WITH(nss-deprecated,dnl
+-AS_HELP_STRING([--with-nss-deprecated],[confirm you realize NSS is going away]),
++AS_HELP_STRING([--with-nss-deprecated],[confirm you realize NSS is going away]),[
+   if test X"$withval" != Xno; then
+     OPT_NSS_AWARE=$withval
+   fi
+-)
++])
+ 
+ OPT_NSS=no
+ AC_ARG_WITH(nss,dnl
+-AS_HELP_STRING([--with-nss=PATH],[where to look for NSS, PATH points to the installation root]),
++AS_HELP_STRING([--with-nss=PATH],[where to look for NSS, PATH points to the installation root]),[
+   OPT_NSS=$withval
+   if test X"$withval" != Xno; then
+ 
+@@ -283,7 +292,7 @@ AS_HELP_STRING([--with-nss=PATH],[where to look for NSS, PATH points to the inst
+ 
+     test -z "TLSCHOICE" || TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }NSS"
+   fi
+-)
++])
+ 
+ dnl If no TLS choice has been made, check if it was explicitly disabled or
+ dnl error out to force the user to decide.
diff --git a/requirements/base.txt b/requirements/base.txt
index 819c723..4bb251e 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1 +1,2 @@
-simplejson>=3.18.4
+simplejson>=3.20.1
+snapshot-restore-py>=1.0.0
diff --git a/scripts/update_deps.sh b/scripts/update_deps.sh
index 4ec4ec1..4799a6f 100755
--- a/scripts/update_deps.sh
+++ b/scripts/update_deps.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-set -e
+set -x
 
 cd deps
 source versions
@@ -8,8 +8,17 @@ source versions
 # Clean up old files
 rm -f aws-lambda-cpp-*.tar.gz && rm -f curl-*.tar.gz
 
+
+LIBCURL="curl-${CURL_MAJOR_VERSION}.${CURL_MINOR_VERSION}.${CURL_PATCH_VERSION}"
+
 # Grab Curl
-wget -c "https://github.com/curl/curl/releases/download/curl-${CURL_MAJOR_VERSION}_${CURL_MINOR_VERSION}_${CURL_PATCH_VERSION}/curl-${CURL_MAJOR_VERSION}.${CURL_MINOR_VERSION}.${CURL_PATCH_VERSION}.tar.gz"
+wget -c "https://github.com/curl/curl/releases/download/curl-${CURL_MAJOR_VERSION}_${CURL_MINOR_VERSION}_${CURL_PATCH_VERSION}/$LIBCURL.tar.gz" -O - | tar -xz
+(
+  cd  $LIBCURL && \
+  patch -p1 < ../patches/libcurl-configure-template.patch
+)
+
+tar -czf $LIBCURL.tar.gz $LIBCURL --no-same-owner && rm -rf $LIBCURL
 
 # Grab aws-lambda-cpp
 wget -c https://github.com/awslabs/aws-lambda-cpp/archive/v$AWS_LAMBDA_CPP_RELEASE.tar.gz -O - | tar -xz
@@ -21,9 +30,10 @@ wget -c https://github.com/awslabs/aws-lambda-cpp/archive/v$AWS_LAMBDA_CPP_RELEA
     patch -p1 < ../patches/aws-lambda-cpp-posting-init-errors.patch && \
     patch -p1 < ../patches/aws-lambda-cpp-make-the-runtime-client-user-agent-overrideable.patch && \
     patch -p1 < ../patches/aws-lambda-cpp-make-lto-optional.patch && \
-    patch -p1 < ../patches/aws-lambda-cpp-add-content-type.patch
+    patch -p1 < ../patches/aws-lambda-cpp-add-content-type.patch && \
+    patch -p1 < ../patches/aws-lambda-cpp-add-tenant-id.patch
 )
 
 ## Pack again and remove the folder
-tar -czvf aws-lambda-cpp-$AWS_LAMBDA_CPP_RELEASE.tar.gz aws-lambda-cpp-$AWS_LAMBDA_CPP_RELEASE --no-same-owner && \
+tar -czf aws-lambda-cpp-$AWS_LAMBDA_CPP_RELEASE.tar.gz aws-lambda-cpp-$AWS_LAMBDA_CPP_RELEASE --no-same-owner && \
   rm -rf aws-lambda-cpp-$AWS_LAMBDA_CPP_RELEASE
diff --git a/setup.py b/setup.py
index 2544b21..2bf28ef 100644
--- a/setup.py
+++ b/setup.py
@@ -84,17 +84,15 @@ def read_requirements(req="base.txt"):
         "Intended Audience :: Developers",
         "Natural Language :: English",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.6",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "License :: OSI Approved :: Apache Software License",
         "Operating System :: OS Independent",
     ],
-    python_requires=">=3.6",
+    python_requires=">=3.9",
     ext_modules=get_runtime_client_extension(),
     test_suite="tests",
 )
diff --git a/tests/integration/codebuild/buildspec.os.alpine.yml b/tests/integration/codebuild/buildspec.os.alpine.yml
index 8d13f9e..8b290f5 100644
--- a/tests/integration/codebuild/buildspec.os.alpine.yml
+++ b/tests/integration/codebuild/buildspec.os.alpine.yml
@@ -15,15 +15,14 @@ batch:
       env:
         variables:
           DISTRO_VERSION:
-            - "3.13"
-            - "3.14"
-            - "3.15"
+            - "3.19"
+            - "3.20"
           RUNTIME_VERSION:
-            - "3.8"
             - "3.9"
             - "3.10"
             - "3.11"
             - "3.12"
+            - "3.13"
 phases:
   pre_build:
     commands:
@@ -52,20 +51,16 @@ phases:
         echo "COPY ${SCRATCH_DIR}/${RIE} /usr/bin/${RIE}" >> \
           "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
       - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
+        echo '{"registry-mirrors": ["https://mirror.gcr.io"]}' > /etc/docker/daemon.json
+        service docker restart
       - echo "Building image ${IMAGE_TAG}"
       - >
         docker build . \
           -f "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp" \
           -t "${IMAGE_TAG}" \
           --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
-          --build-arg DISTRO_VERSION="${DISTRO_VERSION}"
+          --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
+          --load
   build:
     commands:
       - set -x
diff --git a/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml b/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml
deleted file mode 100644
index 91bb021..0000000
--- a/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml
+++ /dev/null
@@ -1,104 +0,0 @@
-version: 0.2
-
-env:
-  variables:
-    OS_DISTRIBUTION: amazonlinux
-    PYTHON_LOCATION: "/usr/local/bin/python3"
-    TEST_NAME: "aws-lambda-python-rtc-amazonlinux-test"
-batch:
-  build-matrix:
-    static:
-      ignore-failure: false
-      env:
-        privileged-mode: true
-    dynamic:
-      env:
-        variables:
-          DISTRO_VERSION:
-            - "1"
-          RUNTIME_VERSION:
-            - "3.7"
-            - "3.8"
-            - "3.9"
-            - "3.10"
-            - "3.11"
-            - "3.12"
-phases:
-  pre_build:
-    commands:
-      - export IMAGE_TAG="python-${OS_DISTRIBUTION}-${DISTRO_VERSION}:${RUNTIME_VERSION}"
-      - echo "Extracting and including the Runtime Interface Emulator"
-      - SCRATCH_DIR=".scratch"
-      - mkdir "${SCRATCH_DIR}"
-      - ARCHITECTURE=$(arch)
-      - tar -xvf tests/integration/resources/aws-lambda-rie.tar.gz --directory "${SCRATCH_DIR}"
-      - >
-        cp "tests/integration/docker/Dockerfile.echo.${OS_DISTRIBUTION}" \
-          "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
-      - >
-        echo "COPY ${SCRATCH_DIR}/aws-lambda-rie /usr/bin/aws-lambda-rie" >> \
-          "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
-      - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
-      - echo "Building image ${IMAGE_TAG}"
-      - >
-        docker build . \
-          -f "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp" \
-          -t "${IMAGE_TAG}" \
-          --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
-          --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
-          --build-arg ARCHITECTURE="${ARCHITECTURE}"
-  build:
-    commands:
-      - set -x
-      - echo "Running Image ${IMAGE_TAG}"
-      - docker network create "${TEST_NAME}-network"
-      - >
-        docker run \
-          --detach \
-          -e "PYTHON_LOCATION=${PYTHON_LOCATION}" \
-          --name "${TEST_NAME}-app" \
-          --network "${TEST_NAME}-network" \
-          --entrypoint="" \
-          "${IMAGE_TAG}" \
-          sh -c '/usr/bin/aws-lambda-rie ${PYTHON_LOCATION} -m awslambdaric app.handler'
-      - sleep 2
-      - >
-        docker run \
-          --name "${TEST_NAME}-tester" \
-          --env "TARGET=${TEST_NAME}-app" \
-          --network "${TEST_NAME}-network" \
-          --entrypoint="" \
-          "${IMAGE_TAG}" \
-          sh -c 'curl -X POST "http://${TARGET}:8080/2015-03-31/functions/function/invocations" -d "{}" --max-time 10'
-      - actual="$(docker logs --tail 1 "${TEST_NAME}-tester" | xargs)"
-      - expected='success'
-      - |
-        echo "Response: ${actual}"
-        if [[ "$actual" != "$expected" ]]; then
-          echo "fail! runtime: $RUNTIME - expected output $expected - got $actual"
-          echo "---------Container Logs: ${TEST_NAME}-app----------"
-          echo
-          docker logs "${TEST_NAME}-app" || true
-          echo
-          echo "---------------------------------------------------"
-          echo "--------Container Logs: ${TEST_NAME}-tester--------"
-          echo
-          docker logs "${TEST_NAME}-tester" || true
-          echo
-          echo "---------------------------------------------------"
-          exit -1
-        fi
-    finally:
-      - echo "Cleaning up..."
-      - docker stop "${TEST_NAME}-app" || true
-      - docker rm --force "${TEST_NAME}-app" || true
-      - docker stop "${TEST_NAME}-tester" || true
-      - docker rm --force "${TEST_NAME}-tester" || true
-      - docker network rm "${TEST_NAME}-network" || true
diff --git a/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml b/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
index 38f2509..05722bb 100644
--- a/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
+++ b/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
@@ -2,7 +2,7 @@ version: 0.2
 
 env:
   variables:
-    OS_DISTRIBUTION: amazonlinux
+    OS_DISTRIBUTION: amazonlinux2
     PYTHON_LOCATION: "/usr/local/bin/python3"
     TEST_NAME: "aws-lambda-python-rtc-amazonlinux-test"
 batch:
@@ -17,12 +17,9 @@ batch:
           DISTRO_VERSION:
             - "2"
           RUNTIME_VERSION:
-            - "3.7"
-            - "3.8"
             - "3.9"
             - "3.10"
             - "3.11"
-            - "3.12"
 phases:
   pre_build:
     commands:
@@ -48,13 +45,8 @@ phases:
         echo "COPY ${SCRATCH_DIR}/${RIE} /usr/bin/${RIE}" >> \
           "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
       - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
+        echo '{"registry-mirrors": ["https://mirror.gcr.io"]}' > /etc/docker/daemon.json
+        service docker restart
       - echo "Building image ${IMAGE_TAG}"
       - >
         docker build . \
@@ -62,7 +54,8 @@ phases:
           -t "${IMAGE_TAG}" \
           --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
           --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
-          --build-arg ARCHITECTURE="${ARCHITECTURE}"
+          --build-arg ARCHITECTURE="${ARCHITECTURE}" \
+          --load
   build:
     commands:
       - set -x
diff --git a/tests/integration/codebuild/buildspec.os.centos.yml b/tests/integration/codebuild/buildspec.os.amazonlinux.2023.yml
similarity index 85%
rename from tests/integration/codebuild/buildspec.os.centos.yml
rename to tests/integration/codebuild/buildspec.os.amazonlinux.2023.yml
index e6930b9..9d6d20f 100644
--- a/tests/integration/codebuild/buildspec.os.centos.yml
+++ b/tests/integration/codebuild/buildspec.os.amazonlinux.2023.yml
@@ -2,9 +2,9 @@ version: 0.2
 
 env:
   variables:
-    OS_DISTRIBUTION: centos
+    OS_DISTRIBUTION: amazonlinux2023
     PYTHON_LOCATION: "/usr/local/bin/python3"
-    TEST_NAME: "aws-lambda-python-rtc-centos-test"
+    TEST_NAME: "aws-lambda-python-rtc-amazonlinux-test"
 batch:
   build-matrix:
     static:
@@ -15,13 +15,10 @@ batch:
       env:
         variables:
           DISTRO_VERSION:
-            - "7"
+            - "2023"
           RUNTIME_VERSION:
-            - "3.8"
-            - "3.9"
-            - "3.10"
-            - "3.11"
             - "3.12"
+            - "3.13"
 phases:
   pre_build:
     commands:
@@ -47,13 +44,8 @@ phases:
         echo "COPY ${SCRATCH_DIR}/${RIE} /usr/bin/${RIE}" >> \
           "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
       - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
+        echo '{"registry-mirrors": ["https://mirror.gcr.io"]}' > /etc/docker/daemon.json
+        service docker restart
       - echo "Building image ${IMAGE_TAG}"
       - >
         docker build . \
@@ -61,7 +53,8 @@ phases:
           -t "${IMAGE_TAG}" \
           --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
           --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
-          --build-arg ARCHITECTURE="${ARCHITECTURE}"
+          --build-arg ARCHITECTURE="${ARCHITECTURE}" \
+          --load
   build:
     commands:
       - set -x
diff --git a/tests/integration/codebuild/buildspec.os.debian.yml b/tests/integration/codebuild/buildspec.os.debian.yml
index 7365f07..44c061f 100644
--- a/tests/integration/codebuild/buildspec.os.debian.yml
+++ b/tests/integration/codebuild/buildspec.os.debian.yml
@@ -15,14 +15,14 @@ batch:
       env:
         variables:
           DISTRO_VERSION:
-            - "buster"
+            - "bookworm"
             - "bullseye"
           RUNTIME_VERSION:
-            - "3.8"
             - "3.9"
             - "3.10"
             - "3.11"
             - "3.12"
+            - "3.13"
 phases:
   pre_build:
     commands:
@@ -51,20 +51,16 @@ phases:
         echo "RUN apt-get update && apt-get install -y curl" >> \
           "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
       - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
+        echo '{"registry-mirrors": ["https://mirror.gcr.io"]}' > /etc/docker/daemon.json
+        service docker restart
       - echo "Building image ${IMAGE_TAG}"
       - >
         docker build . \
           -f "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp" \
           -t "${IMAGE_TAG}" \
           --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
-          --build-arg DISTRO_VERSION="${DISTRO_VERSION}"
+          --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
+          --load
   build:
     commands:
       - set -x
diff --git a/tests/integration/codebuild/buildspec.os.ubuntu.yml b/tests/integration/codebuild/buildspec.os.ubuntu.yml
index 2e312f6..a6e556d 100644
--- a/tests/integration/codebuild/buildspec.os.ubuntu.yml
+++ b/tests/integration/codebuild/buildspec.os.ubuntu.yml
@@ -15,14 +15,14 @@ batch:
       env:
         variables:
           DISTRO_VERSION:
-            - "20.04"
             - "22.04"
+            - "24.04"
           RUNTIME_VERSION:
-            - "3.8"
             - "3.9"
             - "3.10"
             - "3.11"
             - "3.12"
+            - "3.13"
 phases:
   pre_build:
     commands:
@@ -48,20 +48,16 @@ phases:
         echo "COPY ${SCRATCH_DIR}/${RIE} /usr/bin/${RIE}" >> \
           "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp"
       - >
-        if [[ -z "${DOCKERHUB_USERNAME}" && -z "${DOCKERHUB_PASSWORD}" ]];
-        then
-            echo "DockerHub credentials not set as CodeBuild environment variables. Continuing without docker login."
-        else
-            echo "Performing DockerHub login . . ."
-            docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-        fi
+        echo '{"registry-mirrors": ["https://mirror.gcr.io"]}' > /etc/docker/daemon.json
+        service docker restart
       - echo "Building image ${IMAGE_TAG}"
       - >
         docker build . \
           -f "${SCRATCH_DIR}/Dockerfile.echo.${OS_DISTRIBUTION}.tmp" \
           -t "${IMAGE_TAG}" \
           --build-arg RUNTIME_VERSION="${RUNTIME_VERSION}" \
-          --build-arg DISTRO_VERSION="${DISTRO_VERSION}"
+          --build-arg DISTRO_VERSION="${DISTRO_VERSION}" \
+          --load
   build:
     commands:
       - set -x
diff --git a/tests/integration/docker/Dockerfile.echo.alpine b/tests/integration/docker/Dockerfile.echo.alpine
index a53efc0..f6790fa 100644
--- a/tests/integration/docker/Dockerfile.echo.alpine
+++ b/tests/integration/docker/Dockerfile.echo.alpine
@@ -7,7 +7,8 @@ ARG DISTRO_VERSION
 FROM public.ecr.aws/docker/library/python:${RUNTIME_VERSION}-alpine${DISTRO_VERSION} AS python-alpine
 # Install libstdc++
 RUN apk add --no-cache \
-    libstdc++
+    libstdc++ \
+    binutils
 
 
 # Stage 2 - build function and dependencies
@@ -30,6 +31,7 @@ RUN mkdir -p ${RIC_BUILD_DIR}
 # Copy function code and Runtime Interface Client .tgz
 WORKDIR ${RIC_BUILD_DIR}
 COPY . .
+RUN pip3 install setuptools
 RUN make init build test && \
     mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
 
@@ -39,15 +41,12 @@ ARG FUNCTION_DIR="/home/app/"
 RUN mkdir -p ${FUNCTION_DIR}
 # Copy function code
 COPY tests/integration/test-handlers/echo/* ${FUNCTION_DIR}
-# Copy Runtime Interface Client .tgz
-RUN cp ./dist/awslambdaric-test.tar.gz ${FUNCTION_DIR}/awslambdaric-test.tar.gz
 
 # Install the function's dependencies
 WORKDIR ${FUNCTION_DIR}
 RUN python${RUNTIME_VERSION} -m pip install \
-        awslambdaric-test.tar.gz \
-        --target ${FUNCTION_DIR} && \
-    rm awslambdaric-test.tar.gz
+        ${RIC_BUILD_DIR}/dist/awslambdaric-test.tar.gz \
+        --target ${FUNCTION_DIR}
 
 
 # Stage 3 - final runtime interface client image
diff --git a/tests/integration/docker/Dockerfile.echo.amazonlinux b/tests/integration/docker/Dockerfile.echo.amazonlinux2
similarity index 98%
rename from tests/integration/docker/Dockerfile.echo.amazonlinux
rename to tests/integration/docker/Dockerfile.echo.amazonlinux2
index 188de01..be05aa1 100644
--- a/tests/integration/docker/Dockerfile.echo.amazonlinux
+++ b/tests/integration/docker/Dockerfile.echo.amazonlinux2
@@ -17,8 +17,10 @@ RUN yum install -y \
   freetype-devel \
   yum-utils \
   findutils \
-  openssl-devel \
   wget \
+  openssl11 \
+  openssl11-devel \
+  bzip2-devel \
   libffi-devel \
   sqlite-devel
 
@@ -78,6 +80,7 @@ RUN mkdir -p ${RIC_BUILD_DIR}
 # Copy function code and Runtime Interface Client .tgz
 WORKDIR ${RIC_BUILD_DIR}
 COPY . .
+
 RUN make init build test && \
     mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
 
diff --git a/tests/integration/docker/Dockerfile.echo.centos b/tests/integration/docker/Dockerfile.echo.amazonlinux2023
similarity index 77%
rename from tests/integration/docker/Dockerfile.echo.centos
rename to tests/integration/docker/Dockerfile.echo.amazonlinux2023
index e2dd3d0..16bbc79 100644
--- a/tests/integration/docker/Dockerfile.echo.centos
+++ b/tests/integration/docker/Dockerfile.echo.amazonlinux2023
@@ -1,13 +1,12 @@
 ARG DISTRO_VERSION
-
 # Stage 1 - bundle base image + runtime interface client
 # Grab a fresh copy of the image and install Python
-FROM public.ecr.aws/docker/library/centos:${DISTRO_VERSION} AS python-centos-builder
+FROM public.ecr.aws/amazonlinux/amazonlinux:${DISTRO_VERSION} AS python-amazonlinux-builder
 
 ARG RUNTIME_VERSION
 
 # Install apt dependencies
-RUN yum install -y \
+RUN dnf install -y \
   gcc \
   gcc-c++ \
   tar \
@@ -18,8 +17,10 @@ RUN yum install -y \
   freetype-devel \
   yum-utils \
   findutils \
-  openssl-devel \
   wget \
+  openssl \
+  openssl-devel \
+  bzip2-devel \
   libffi-devel \
   sqlite-devel
 
@@ -39,21 +40,21 @@ RUN RUNTIME_LATEST_VERSION=${RUNTIME_VERSION}.$(curl -s https://www.python.org/f
   && ln -s /usr/local/bin/python${RUNTIME_VERSION} /usr/local/bin/python${RUNTIME_LATEST_VERSION}
 
 # Stage 2 - clean python build dependencies
-FROM public.ecr.aws/docker/library/centos:${DISTRO_VERSION} AS python-centos
-RUN yum install -y \
+FROM public.ecr.aws/amazonlinux/amazonlinux:${DISTRO_VERSION} AS python-amazonlinux
+RUN dnf install -y \
   libffi-devel
 
 # Copy the compiled python to /usr/local
-COPY --from=python-centos-builder /usr/local /usr/local
+COPY --from=python-amazonlinux-builder /usr/local /usr/local
 ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
 
 # Stage 3 - build function and dependencies
-FROM python-centos-builder AS build-image
+FROM python-amazonlinux-builder AS build-image
 ARG RUNTIME_VERSION
 ARG ARCHITECTURE
 
 # Install aws-lambda-cpp build dependencies
-RUN yum install -y \
+RUN dnf install -y \
   tar \
   gzip \
   make \
@@ -62,7 +63,8 @@ RUN yum install -y \
   libtool \
   libcurl-devel \
   gcc-c++ \
-  wget
+  wget \
+  sqlite-devel
 
 # Install a modern CMake
 RUN wget --quiet -O cmake-install https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-linux-${ARCHITECTURE}.sh && \
@@ -79,8 +81,19 @@ RUN mkdir -p ${RIC_BUILD_DIR}
 # Copy function code and Runtime Interface Client .tgz
 WORKDIR ${RIC_BUILD_DIR}
 COPY . .
-RUN make init build && \
-    mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
+
+# distutils no longer available in python3.12 and later
+#   https://docs.python.org/3/whatsnew/3.12.html
+#   https://peps.python.org/pep-0632/
+RUN pip3 install setuptools
+RUN make init build 
+
+RUN mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
+RUN python${RUNTIME_VERSION} -m pip install \
+      ./dist/awslambdaric-test.tar.gz \
+      --target ${RIC_BUILD_DIR}
+
+RUN make test
 
 # Include global args in this stage of the build
 ARG FUNCTION_DIR="/home/app/"
@@ -93,19 +106,16 @@ RUN cp ./dist/awslambdaric-test.tar.gz ${FUNCTION_DIR}/awslambdaric-test.tar.gz
 
 # Install the function's dependencies
 WORKDIR ${FUNCTION_DIR}
-ARG ENABLE_LTO=OFF
-ENV ENABLE_LTO ${ENABLE_LTO}
 RUN python${RUNTIME_VERSION} -m pip install \
-          awslambdaric-test.tar.gz \
-          --verbose \
-          --target ${FUNCTION_DIR} && \
+        awslambdaric-test.tar.gz \
+        --target ${FUNCTION_DIR} && \
     rm awslambdaric-test.tar.gz
 
 
 # Stage 4 - final runtime interface client image
 # Grab a fresh copy of the Python image
-FROM python-centos
-
+FROM python-amazonlinux
+RUN dnf install -y brotli
 # Include global arg in this stage of the build
 ARG FUNCTION_DIR="/home/app/"
 # Set working directory to function root directory
diff --git a/tests/integration/docker/Dockerfile.echo.debian b/tests/integration/docker/Dockerfile.echo.debian
index 8ac660b..bf0f4fa 100644
--- a/tests/integration/docker/Dockerfile.echo.debian
+++ b/tests/integration/docker/Dockerfile.echo.debian
@@ -19,6 +19,7 @@ RUN mkdir -p ${RIC_BUILD_DIR}
 # Copy function code and Runtime Interface Client .tgz
 WORKDIR ${RIC_BUILD_DIR}
 COPY . .
+RUN pip3 install setuptools
 RUN make init build test && \
     mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
 
diff --git a/tests/integration/docker/Dockerfile.echo.ubuntu b/tests/integration/docker/Dockerfile.echo.ubuntu
index 692b3f2..0ce3000 100644
--- a/tests/integration/docker/Dockerfile.echo.ubuntu
+++ b/tests/integration/docker/Dockerfile.echo.ubuntu
@@ -9,40 +9,41 @@ ENV DEBIAN_FRONTEND=noninteractive
 ARG RUNTIME_VERSION
 
 # Install python and pip
-RUN apt-get update && \
-  apt-get install -y \
-    software-properties-common
+RUN apt-get update && apt-get install -y software-properties-common
 RUN add-apt-repository ppa:deadsnakes/ppa
 RUN apt-get update && \
-  apt-get install -y \
-    curl \
-    python${RUNTIME_VERSION} \
-    python${RUNTIME_VERSION}-distutils
+    apt-get install -y \
+        curl \
+        python${RUNTIME_VERSION} \
+        python3-pip \
+        python3-virtualenv
+
+# python3xx-distutils is needed for python < 3.12
+RUN if [ $(echo ${RUNTIME_VERSION} | cut -d '.' -f 2) -lt 12 ]; then \
+            apt-get install -y python${RUNTIME_VERSION}-distutils; \
+    fi
+RUN virtualenv --python /usr/bin/python${RUNTIME_VERSION} --no-setuptools /home/venv
+
 
-RUN ln -s /usr/bin/python${RUNTIME_VERSION} /usr/local/bin/python3
 
 # Stage 2 - build function and dependencies
 FROM python-image AS python-ubuntu-builder
 
 ARG RUNTIME_VERSION
 
-RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
-RUN python${RUNTIME_VERSION} get-pip.py
-
 # Install aws-lambda-cpp build dependencies
-RUN apt-get update && \
-  apt-get install -y \
-    g++ \
-    gcc \
-    tar \
-    gzip \
-    make \
-    cmake \
-    autoconf \
-    automake \
-    libtool \
-    libcurl4-openssl-dev \
-    python${RUNTIME_VERSION}-dev
+RUN apt-get install -y \
+        g++ \
+        gcc \
+        tar \
+        gzip \
+        make \
+        cmake \
+        autoconf \
+        automake \
+        libtool \
+        libcurl4-openssl-dev \
+        python${RUNTIME_VERSION}-dev
 
 # Include global args in this stage of the build
 ARG RIC_BUILD_DIR="/home/build/"
@@ -51,27 +52,28 @@ RUN mkdir -p ${RIC_BUILD_DIR}
 # Copy function code and Runtime Interface Client .tgz
 WORKDIR ${RIC_BUILD_DIR}
 COPY . .
-RUN make init build test && \
+RUN . /home/venv/bin/activate && \
+    pip install setuptools && \
+    make init build test && \
     mv ./dist/awslambdaric-*.tar.gz ./dist/awslambdaric-test.tar.gz
 
+
+
 # Include global args in this stage of the build
 ARG FUNCTION_DIR="/home/app/"
 # Create function directory
 RUN mkdir -p ${FUNCTION_DIR}
 # Copy function code
 COPY tests/integration/test-handlers/echo/* ${FUNCTION_DIR}
-# Copy Runtime Interface Client .tgz
-RUN cp ./dist/awslambdaric-test.tar.gz ${FUNCTION_DIR}/awslambdaric-test.tar.gz
-
 # Install the function's dependencies
 WORKDIR ${FUNCTION_DIR}
-RUN python${RUNTIME_VERSION} -m pip install \
-        awslambdaric-test.tar.gz \
-        --target ${FUNCTION_DIR} && \
-    rm awslambdaric-test.tar.gz
+RUN . /home/venv/bin/activate && \
+    pip install ${RIC_BUILD_DIR}/dist/awslambdaric-test.tar.gz --target ${FUNCTION_DIR}
+
+
 
 
-# Stage 4 - final runtime interface client image
+# Stage 3 - final runtime interface client image
 # Grab a fresh copy of the Python image
 FROM python-image
 
diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py
index 7bc2ad2..33afb1c 100644
--- a/tests/test_bootstrap.py
+++ b/tests/test_bootstrap.py
@@ -14,15 +14,20 @@
 import unittest
 from io import StringIO
 from tempfile import NamedTemporaryFile
-from unittest.mock import MagicMock, Mock, patch
+from unittest.mock import MagicMock, Mock, patch, ANY
 
 import awslambdaric.bootstrap as bootstrap
 from awslambdaric.lambda_runtime_exception import FaultException
-from awslambdaric.lambda_runtime_log_utils import LogFormat, _get_log_level_from_env_var
+from awslambdaric.lambda_runtime_log_utils import (
+    LogFormat,
+    _get_log_level_from_env_var,
+    JsonFormatter,
+)
 from awslambdaric.lambda_runtime_marshaller import LambdaMarshaller
 from awslambdaric.lambda_literals import (
     lambda_unhandled_exception_warning_message,
 )
+import snapshot_restore_py
 
 
 class TestUpdateXrayEnv(unittest.TestCase):
@@ -60,6 +65,14 @@ def setUp(self):
         self.event_body = '"event_body"'
         self.working_directory = os.getcwd()
 
+        logging.getLogger().handlers.clear()
+
+    def tearDown(self) -> None:
+        logging.getLogger().handlers.clear()
+        logging.getLogger().level = logging.NOTSET
+
+        return super().tearDown()
+
     @staticmethod
     def dummy_handler(json_input, lambda_context):
         return {"input": json_input, "aws_request_id": lambda_context.aws_request_id}
@@ -75,6 +88,7 @@ def test_handle_event_request_happy_case(self):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         self.lambda_runtime.post_invocation_result.assert_called_once_with(
@@ -98,6 +112,7 @@ def test_handle_event_request_invalid_client_context(self):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -139,6 +154,7 @@ def test_handle_event_request_invalid_cognito_idenity(self):
             "invalid_cognito_identity",
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -181,6 +197,7 @@ def test_handle_event_request_invalid_event_body(self):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -225,6 +242,7 @@ def invalid_json_response(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -270,6 +288,7 @@ def __init__(self, message):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -322,6 +341,7 @@ def __init__(self, message):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -373,6 +393,7 @@ def unable_to_import_module(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -412,6 +433,7 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
         args, _ = self.lambda_runtime.post_invocation_error.call_args
@@ -450,6 +472,8 @@ def raise_exception_handler(json_input, lambda_context):
                     ),
                 )
 
+        logging.getLogger().addHandler(logging.StreamHandler(mock_stdout))
+
         bootstrap.handle_event_request(
             self.lambda_runtime,
             raise_exception_handler,
@@ -460,14 +484,12 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
 
         # NOTE: Indentation characters are NO-BREAK SPACE (U+00A0) not SPACE (U+0020)
-        error_logs = (
-            lambda_unhandled_exception_warning_message
-            + "[ERROR] FaultExceptionType: Fault exception msg\r"
-        )
+        error_logs = "[ERROR] FaultExceptionType: Fault exception msg\r"
         error_logs += "Traceback (most recent call last):\r"
         error_logs += '  File "spam.py", line 3, in <module>\r'
         error_logs += "    spam.eggs()\r"
@@ -486,6 +508,8 @@ def raise_exception_handler(json_input, lambda_context):
                     "FaultExceptionType", "Fault exception msg", None
                 )
 
+        logging.getLogger().addHandler(logging.StreamHandler(mock_stdout))
+
         bootstrap.handle_event_request(
             self.lambda_runtime,
             raise_exception_handler,
@@ -496,12 +520,10 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
-        error_logs = (
-            lambda_unhandled_exception_warning_message
-            + "[ERROR] FaultExceptionType: Fault exception msg\rTraceback (most recent call last):\n"
-        )
+        error_logs = "[ERROR] FaultExceptionType: Fault exception msg\rTraceback (most recent call last):\n"
 
         self.assertEqual(mock_stdout.getvalue(), error_logs)
 
@@ -515,6 +537,8 @@ def raise_exception_handler(json_input, lambda_context):
             except ImportError:
                 raise bootstrap.FaultException("FaultExceptionType", None, None)
 
+        logging.getLogger().addHandler(logging.StreamHandler(mock_stdout))
+
         bootstrap.handle_event_request(
             self.lambda_runtime,
             raise_exception_handler,
@@ -525,12 +549,10 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
-        error_logs = (
-            lambda_unhandled_exception_warning_message
-            + "[ERROR] FaultExceptionType\rTraceback (most recent call last):\n"
-        )
+        error_logs = "[ERROR] FaultExceptionType\rTraceback (most recent call last):\n"
 
         self.assertEqual(mock_stdout.getvalue(), error_logs)
 
@@ -544,6 +566,8 @@ def raise_exception_handler(json_input, lambda_context):
             except ImportError:
                 raise bootstrap.FaultException(None, "Fault exception msg", None)
 
+        logging.getLogger().addHandler(logging.StreamHandler(mock_stdout))
+
         bootstrap.handle_event_request(
             self.lambda_runtime,
             raise_exception_handler,
@@ -554,12 +578,10 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
-        error_logs = (
-            lambda_unhandled_exception_warning_message
-            + "[ERROR] Fault exception msg\rTraceback (most recent call last):\n"
-        )
+        error_logs = "[ERROR] Fault exception msg\rTraceback (most recent call last):\n"
 
         self.assertEqual(mock_stdout.getvalue(), error_logs)
 
@@ -582,6 +604,8 @@ def raise_exception_handler(json_input, lambda_context):
                     ),
                 )
 
+        logging.getLogger().addHandler(logging.StreamHandler(mock_stdout))
+
         bootstrap.handle_event_request(
             self.lambda_runtime,
             raise_exception_handler,
@@ -592,9 +616,10 @@ def raise_exception_handler(json_input, lambda_context):
             {},
             "invoked_function_arn",
             0,
+            "tenant_id",
             bootstrap.StandardLogSink(),
         )
-        error_logs = lambda_unhandled_exception_warning_message + "[ERROR]\r"
+        error_logs = "[ERROR]\r"
         error_logs += "Traceback (most recent call last):\r"
         error_logs += '  File "spam.py", line 3, in <module>\r'
         error_logs += "    spam.eggs()\r"
@@ -603,6 +628,39 @@ def raise_exception_handler(json_input, lambda_context):
 
         self.assertEqual(mock_stdout.getvalue(), error_logs)
 
+    @patch("sys.stdout", new_callable=StringIO)
+    def test_handle_event_request_fault_exception_logging_in_json(self, mock_stdout):
+        def raise_exception_handler(json_input, lambda_context):
+            try:
+                import invalid_module  # noqa: F401
+            except ImportError:
+                raise bootstrap.FaultException("FaultExceptionType", None, None)
+
+        logging_handler = logging.StreamHandler(mock_stdout)
+        logging_handler.setFormatter(JsonFormatter())
+        logging.getLogger().addHandler(logging_handler)
+
+        bootstrap.handle_event_request(
+            self.lambda_runtime,
+            raise_exception_handler,
+            "invoke_id",
+            self.event_body,
+            "application/json",
+            {},
+            {},
+            "invoked_function_arn",
+            0,
+            "tenant_id",
+            bootstrap.StandardLogSink(),
+        )
+
+        stdout_value = mock_stdout.getvalue()
+
+        # this line is not in json because of the way the test runtime is bootstrapped
+        error_logs = "[ERROR] FaultExceptionType\rTraceback (most recent call last):\n"
+
+        self.assertEqual(stdout_value, error_logs)
+
 
 class TestXrayFault(unittest.TestCase):
     def test_make_xray(self):
@@ -799,6 +857,7 @@ def test_application_json(self):
             cognito_identity_json=None,
             invoked_function_arn="invocation-arn",
             epoch_deadline_time_in_ms=1415836801003,
+            tenant_id=None,
             log_sink=bootstrap.StandardLogSink(),
         )
 
@@ -818,6 +877,7 @@ def test_binary_request_binary_response(self):
             cognito_identity_json=None,
             invoked_function_arn="invocation-arn",
             epoch_deadline_time_in_ms=1415836801003,
+            tenant_id=None,
             log_sink=bootstrap.StandardLogSink(),
         )
 
@@ -837,6 +897,7 @@ def test_json_request_binary_response(self):
             cognito_identity_json=None,
             invoked_function_arn="invocation-arn",
             epoch_deadline_time_in_ms=1415836801003,
+            tenant_id=None,
             log_sink=bootstrap.StandardLogSink(),
         )
 
@@ -855,6 +916,7 @@ def test_binary_with_application_json(self):
             cognito_identity_json=None,
             invoked_function_arn="invocation-arn",
             epoch_deadline_time_in_ms=1415836801003,
+            tenant_id=None,
             log_sink=bootstrap.StandardLogSink(),
         )
 
@@ -1288,6 +1350,31 @@ def test_json_formatter(self, mock_stderr):
                     )
         self.assertEqual(mock_stderr.getvalue(), "")
 
+    @patch("awslambdaric.bootstrap._GLOBAL_TENANT_ID", "test-tenant-id")
+    @patch("sys.stderr", new_callable=StringIO)
+    def test_json_formatter_with_tenant_id(self, mock_stderr):
+        logger = logging.getLogger("a.b")
+        level = logging.INFO
+        message = "Test json formatting with tenant id"
+        expected = {
+            "level": "INFO",
+            "logger": "a.b",
+            "message": message,
+            "requestId": "",
+            "tenantId": "test-tenant-id",
+        }
+
+        with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
+            logger.log(level, message)
+
+            data = json.loads(mock_stdout.getvalue())
+            data.pop("timestamp")
+            self.assertEqual(
+                data,
+                expected,
+            )
+        self.assertEqual(mock_stderr.getvalue(), "")
+
     @patch("sys.stdout", new_callable=StringIO)
     @patch("sys.stderr", new_callable=StringIO)
     def test_exception(self, mock_stderr, mock_stdout):
@@ -1457,5 +1544,53 @@ class TestException(Exception):
         mock_sys.exit.assert_called_once_with(1)
 
 
+class TestOnInitComplete(unittest.TestCase):
+    def tearDown(self):
+        # We are accessing private filed for cleaning up
+        snapshot_restore_py._before_snapshot_registry = []
+        snapshot_restore_py._after_restore_registry = []
+
+    # We are using ANY over here as the main thing we want to test is teh errorType propogation and stack trace generation
+    error_result = {
+        "errorMessage": "This is a Dummy type error",
+        "errorType": "TypeError",
+        "requestId": "",
+        "stackTrace": ANY,
+    }
+
+    def raise_type_error(self):
+        raise TypeError("This is a Dummy type error")
+
+    @patch("awslambdaric.bootstrap.LambdaRuntimeClient")
+    def test_before_snapshot_exception(self, mock_runtime_client):
+        snapshot_restore_py.register_before_snapshot(self.raise_type_error)
+
+        with self.assertRaises(SystemExit) as cm:
+            bootstrap.on_init_complete(
+                mock_runtime_client, log_sink=bootstrap.StandardLogSink()
+            )
+
+        self.assertEqual(cm.exception.code, 64)
+        mock_runtime_client.post_init_error.assert_called_once_with(
+            self.error_result,
+            FaultException.BEFORE_SNAPSHOT_ERROR,
+        )
+
+    @patch("awslambdaric.bootstrap.LambdaRuntimeClient")
+    def test_after_restore_exception(self, mock_runtime_client):
+        snapshot_restore_py.register_after_restore(self.raise_type_error)
+
+        with self.assertRaises(SystemExit) as cm:
+            bootstrap.on_init_complete(
+                mock_runtime_client, log_sink=bootstrap.StandardLogSink()
+            )
+
+        self.assertEqual(cm.exception.code, 65)
+        mock_runtime_client.restore_next.assert_called_once()
+        mock_runtime_client.report_restore_error.assert_called_once_with(
+            self.error_result
+        )
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_lambda_context.py b/tests/test_lambda_context.py
index 34d59da..f7959ab 100644
--- a/tests/test_lambda_context.py
+++ b/tests/test_lambda_context.py
@@ -37,6 +37,7 @@ def test_init(self):
         self.assertEqual(context.memory_limit_in_mb, "1234")
         self.assertEqual(context.function_version, "version1")
         self.assertEqual(context.invoked_function_arn, "arn:test1")
+        self.assertEqual(context.tenant_id, None)
         self.assertEqual(context.identity.cognito_identity_id, None)
         self.assertEqual(context.identity.cognito_identity_pool_id, None)
         self.assertEqual(context.client_context.client.installation_id, None)
@@ -74,6 +75,21 @@ def test_init_cognito(self):
         self.assertEqual(context.identity.cognito_identity_id, "id1")
         self.assertEqual(context.identity.cognito_identity_pool_id, "poolid1")
 
+    def test_init_tenant_id(self):
+        client_context = {}
+        cognito_identity = {}
+        tenant_id = "blue"
+
+        context = LambdaContext(
+            "invoke-id1",
+            client_context,
+            cognito_identity,
+            1415836801000,
+            "arn:test",
+            tenant_id,
+        )
+        self.assertEqual(context.tenant_id, "blue")
+
     def test_init_client_context(self):
         client_context = {
             "client": {
diff --git a/tests/test_lambda_runtime_client.py b/tests/test_lambda_runtime_client.py
index b0eae4a..fc4af65 100644
--- a/tests/test_lambda_runtime_client.py
+++ b/tests/test_lambda_runtime_client.py
@@ -14,6 +14,7 @@
     LambdaRuntimeClientError,
     _user_agent,
 )
+from awslambdaric.lambda_runtime_marshaller import to_json
 
 
 class TestInvocationRequest(unittest.TestCase):
@@ -25,6 +26,7 @@ def test_constructor(self):
             deadline_time_in_ms="Lambda-Runtime-Deadline-Ms",
             client_context="Lambda-Runtime-Client-Context",
             cognito_identity="Lambda-Runtime-Cognito-Identity",
+            tenant_id="Lambda-Runtime-Aws-Tenant-Id",
             content_type="Content-Type",
             event_body="response_body",
         )
@@ -36,6 +38,7 @@ def test_constructor(self):
             deadline_time_in_ms="Lambda-Runtime-Deadline-Ms",
             client_context="Lambda-Runtime-Client-Context",
             cognito_identity="Lambda-Runtime-Cognito-Identity",
+            tenant_id="Lambda-Runtime-Aws-Tenant-Id",
             content_type="Content-Type",
             event_body="response_body",
         )
@@ -47,6 +50,7 @@ def test_constructor(self):
             deadline_time_in_ms="Lambda-Runtime-Deadline-Ms",
             client_context="Lambda-Runtime-Client-Context",
             cognito_identity="Lambda-Runtime-Cognito-Identity",
+            tenant_id="Lambda-Runtime-Aws-Tenant-Id",
             content_type="Content-Type",
             event_body="another_response_body",
         )
@@ -67,6 +71,7 @@ def test_wait_next_invocation(self, mock_runtime_client):
             "Lambda-Runtime-Deadline-Ms": 12,
             "Lambda-Runtime-Client-Context": "client_context",
             "Lambda-Runtime-Cognito-Identity": "cognito_identity",
+            "Lambda-Runtime-Aws-Tenant-Id": "tenant_id",
             "Content-Type": "application/json",
         }
         mock_runtime_client.next.return_value = response_body, headears
@@ -81,6 +86,7 @@ def test_wait_next_invocation(self, mock_runtime_client):
         self.assertEqual(event_request.deadline_time_in_ms, 12)
         self.assertEqual(event_request.client_context, "client_context")
         self.assertEqual(event_request.cognito_identity, "cognito_identity")
+        self.assertEqual(event_request.tenant_id, "tenant_id")
         self.assertEqual(event_request.content_type, "application/json")
         self.assertEqual(event_request.event_body, response_body)
 
@@ -96,9 +102,101 @@ def test_wait_next_invocation(self, mock_runtime_client):
         self.assertEqual(event_request.deadline_time_in_ms, 12)
         self.assertEqual(event_request.client_context, "client_context")
         self.assertEqual(event_request.cognito_identity, "cognito_identity")
+        self.assertEqual(event_request.tenant_id, "tenant_id")
         self.assertEqual(event_request.content_type, "application/json")
         self.assertEqual(event_request.event_body, response_body)
 
+    @patch("awslambdaric.lambda_runtime_client.runtime_client")
+    def test_wait_next_invocation_without_tenant_id_header(self, mock_runtime_client):
+        response_body = b"{}"
+        headers = {
+            "Lambda-Runtime-Aws-Request-Id": "RID1234",
+            "Lambda-Runtime-Trace-Id": "TID1234",
+            "Lambda-Runtime-Invoked-Function-Arn": "FARN1234",
+            "Lambda-Runtime-Deadline-Ms": 12,
+            "Lambda-Runtime-Client-Context": "client_context",
+            "Lambda-Runtime-Cognito-Identity": "cognito_identity",
+            "Content-Type": "application/json",
+        }
+        mock_runtime_client.next.return_value = response_body, headers
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+
+        event_request = runtime_client.wait_next_invocation()
+
+        self.assertIsNotNone(event_request)
+        self.assertIsNone(event_request.tenant_id)
+        self.assertEqual(event_request.event_body, response_body)
+
+    @patch("awslambdaric.lambda_runtime_client.runtime_client")
+    def test_wait_next_invocation_with_null_tenant_id_header(self, mock_runtime_client):
+        response_body = b"{}"
+        headers = {
+            "Lambda-Runtime-Aws-Request-Id": "RID1234",
+            "Lambda-Runtime-Trace-Id": "TID1234",
+            "Lambda-Runtime-Invoked-Function-Arn": "FARN1234",
+            "Lambda-Runtime-Deadline-Ms": 12,
+            "Lambda-Runtime-Client-Context": "client_context",
+            "Lambda-Runtime-Cognito-Identity": "cognito_identity",
+            "Lambda-Runtime-Aws-Tenant-Id": None,
+            "Content-Type": "application/json",
+        }
+        mock_runtime_client.next.return_value = response_body, headers
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+
+        event_request = runtime_client.wait_next_invocation()
+
+        self.assertIsNotNone(event_request)
+        self.assertIsNone(event_request.tenant_id)
+        self.assertEqual(event_request.event_body, response_body)
+
+    @patch("awslambdaric.lambda_runtime_client.runtime_client")
+    def test_wait_next_invocation_with_empty_tenant_id_header(
+        self, mock_runtime_client
+    ):
+        response_body = b"{}"
+        headers = {
+            "Lambda-Runtime-Aws-Request-Id": "RID1234",
+            "Lambda-Runtime-Trace-Id": "TID1234",
+            "Lambda-Runtime-Invoked-Function-Arn": "FARN1234",
+            "Lambda-Runtime-Deadline-Ms": 12,
+            "Lambda-Runtime-Client-Context": "client_context",
+            "Lambda-Runtime-Cognito-Identity": "cognito_identity",
+            "Lambda-Runtime-Aws-Tenant-Id": "",
+            "Content-Type": "application/json",
+        }
+        mock_runtime_client.next.return_value = response_body, headers
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+
+        event_request = runtime_client.wait_next_invocation()
+
+        self.assertIsNotNone(event_request)
+        self.assertEqual(event_request.tenant_id, "")
+        self.assertEqual(event_request.event_body, response_body)
+
+    error_result = {
+        "errorMessage": "Dummy message",
+        "errorType": "Runtime.DummyError",
+        "requestId": "",
+        "stackTrace": [],
+    }
+
+    headers = {"Lambda-Runtime-Function-Error-Type": error_result["errorType"]}
+
+    restore_error_result = {
+        "errorMessage": "Dummy Restore error",
+        "errorType": "Runtime.DummyRestoreError",
+        "requestId": "",
+        "stackTrace": [],
+    }
+
+    restore_error_header = {
+        "Lambda-Runtime-Function-Error-Type": "Runtime.AfterRestoreError"
+    }
+
+    before_snapshot_error_header = {
+        "Lambda-Runtime-Function-Error-Type": "Runtime.BeforeSnapshotError"
+    }
+
     @patch("http.client.HTTPConnection", autospec=http.client.HTTPConnection)
     def test_post_init_error(self, MockHTTPConnection):
         mock_conn = MockHTTPConnection.return_value
@@ -108,11 +206,14 @@ def test_post_init_error(self, MockHTTPConnection):
         mock_response.code = http.HTTPStatus.ACCEPTED
 
         runtime_client = LambdaRuntimeClient("localhost:1234")
-        runtime_client.post_init_error("error_data")
+        runtime_client.post_init_error(self.error_result)
 
         MockHTTPConnection.assert_called_with("localhost:1234")
         mock_conn.request.assert_called_once_with(
-            "POST", "/2018-06-01/runtime/init/error", "error_data"
+            "POST",
+            "/2018-06-01/runtime/init/error",
+            to_json(self.error_result),
+            headers=self.headers,
         )
         mock_response.read.assert_called_once()
 
@@ -127,7 +228,7 @@ def test_post_init_error_non_accepted_status_code(self, MockHTTPConnection):
         runtime_client = LambdaRuntimeClient("localhost:1234")
 
         with self.assertRaises(LambdaRuntimeClientError) as cm:
-            runtime_client.post_init_error("error_data")
+            runtime_client.post_init_error(self.error_result)
         returned_exception = cm.exception
 
         self.assertEqual(returned_exception.endpoint, "/2018-06-01/runtime/init/error")
@@ -212,15 +313,73 @@ def test_post_invocation_error_with_too_large_xray_cause(self, mock_runtime_clie
             invoke_id, error_data, ""
         )
 
+    @patch("http.client.HTTPConnection", autospec=http.client.HTTPConnection)
+    def test_restore_next(self, MockHTTPConnection):
+        mock_conn = MockHTTPConnection.return_value
+        mock_response = MagicMock(autospec=http.client.HTTPResponse)
+        mock_conn.getresponse.return_value = mock_response
+        mock_response.read.return_value = b""
+        mock_response.code = http.HTTPStatus.OK
+
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+        runtime_client.restore_next()
+
+        MockHTTPConnection.assert_called_with("localhost:1234")
+        mock_conn.request.assert_called_once_with(
+            "GET",
+            "/2018-06-01/runtime/restore/next",
+        )
+        mock_response.read.assert_called_once()
+
+    @patch("http.client.HTTPConnection", autospec=http.client.HTTPConnection)
+    def test_restore_error(self, MockHTTPConnection):
+        mock_conn = MockHTTPConnection.return_value
+        mock_response = MagicMock(autospec=http.client.HTTPResponse)
+        mock_conn.getresponse.return_value = mock_response
+        mock_response.read.return_value = b""
+        mock_response.code = http.HTTPStatus.ACCEPTED
+
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+        runtime_client.report_restore_error(self.restore_error_result)
+
+        MockHTTPConnection.assert_called_with("localhost:1234")
+        mock_conn.request.assert_called_once_with(
+            "POST",
+            "/2018-06-01/runtime/restore/error",
+            to_json(self.restore_error_result),
+            headers=self.restore_error_header,
+        )
+        mock_response.read.assert_called_once()
+
+    @patch("http.client.HTTPConnection", autospec=http.client.HTTPConnection)
+    def test_init_before_snapshot_error(self, MockHTTPConnection):
+        mock_conn = MockHTTPConnection.return_value
+        mock_response = MagicMock(autospec=http.client.HTTPResponse)
+        mock_conn.getresponse.return_value = mock_response
+        mock_response.read.return_value = b""
+        mock_response.code = http.HTTPStatus.ACCEPTED
+
+        runtime_client = LambdaRuntimeClient("localhost:1234")
+        runtime_client.post_init_error(self.error_result, "Runtime.BeforeSnapshotError")
+
+        MockHTTPConnection.assert_called_with("localhost:1234")
+        mock_conn.request.assert_called_once_with(
+            "POST",
+            "/2018-06-01/runtime/init/error",
+            to_json(self.error_result),
+            headers=self.before_snapshot_error_header,
+        )
+        mock_response.read.assert_called_once()
+
     def test_connection_refused(self):
         with self.assertRaises(ConnectionRefusedError):
             runtime_client = LambdaRuntimeClient("127.0.0.1:1")
-            runtime_client.post_init_error("error")
+            runtime_client.post_init_error(self.error_result)
 
     def test_invalid_addr(self):
         with self.assertRaises(OSError):
             runtime_client = LambdaRuntimeClient("::::")
-            runtime_client.post_init_error("error")
+            runtime_client.post_init_error(self.error_result)
 
     def test_lambdaric_version(self):
         self.assertTrue(_user_agent().endswith(__version__))
diff --git a/tests/test_lambda_runtime_marshaller.py b/tests/test_lambda_runtime_marshaller.py
index 7cd73b4..843bcee 100644
--- a/tests/test_lambda_runtime_marshaller.py
+++ b/tests/test_lambda_runtime_marshaller.py
@@ -11,13 +11,17 @@
 
 class TestLambdaRuntimeMarshaller(unittest.TestCase):
     execution_envs = (
+        "AWS_Lambda_python3.13",
         "AWS_Lambda_python3.12",
         "AWS_Lambda_python3.11",
         "AWS_Lambda_python3.10",
         "AWS_Lambda_python3.9",
     )
 
-    envs_lambda_marshaller_ensure_ascii_false = {"AWS_Lambda_python3.12"}
+    envs_lambda_marshaller_ensure_ascii_false = {
+        "AWS_Lambda_python3.12",
+        "AWS_Lambda_python3.13",
+    }
 
     execution_envs_lambda_marshaller_ensure_ascii_true = tuple(
         set(execution_envs).difference(envs_lambda_marshaller_ensure_ascii_false)
diff --git a/tests/test_runtime_hooks.py b/tests/test_runtime_hooks.py
new file mode 100644
index 0000000..e73204f
--- /dev/null
+++ b/tests/test_runtime_hooks.py
@@ -0,0 +1,65 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+import unittest
+from unittest.mock import patch, call
+from awslambdaric import lambda_runtime_hooks_runner
+import snapshot_restore_py
+
+
+def fun_test1():
+    print("In function ONE")
+
+
+def fun_test2():
+    print("In function TWO")
+
+
+def fun_with_args_kwargs(x, y, **kwargs):
+    print("Here are the args:", x, y)
+    print("Here are the keyword args:", kwargs)
+
+
+class TestRuntimeHooks(unittest.TestCase):
+    def tearDown(self):
+        # We are accessing private filed for cleaning up
+        snapshot_restore_py._before_snapshot_registry = []
+        snapshot_restore_py._after_restore_registry = []
+
+    @patch("builtins.print")
+    def test_before_snapshot_execution_order(self, mock_print):
+        snapshot_restore_py.register_before_snapshot(
+            fun_with_args_kwargs, 5, 7, arg1="Lambda", arg2="SnapStart"
+        )
+        snapshot_restore_py.register_before_snapshot(fun_test2)
+        snapshot_restore_py.register_before_snapshot(fun_test1)
+
+        lambda_runtime_hooks_runner.run_before_snapshot()
+
+        calls = []
+        calls.append(call("In function ONE"))
+        calls.append(call("In function TWO"))
+        calls.append(call("Here are the args:", 5, 7))
+        calls.append(
+            call("Here are the keyword args:", {"arg1": "Lambda", "arg2": "SnapStart"})
+        )
+        self.assertEqual(calls, mock_print.mock_calls)
+
+    @patch("builtins.print")
+    def test_after_restore_execution_order(self, mock_print):
+        snapshot_restore_py.register_after_restore(
+            fun_with_args_kwargs, 11, 13, arg1="Lambda", arg2="SnapStart"
+        )
+        snapshot_restore_py.register_after_restore(fun_test2)
+        snapshot_restore_py.register_after_restore(fun_test1)
+
+        lambda_runtime_hooks_runner.run_after_restore()
+
+        calls = []
+        calls.append(call("Here are the args:", 11, 13))
+        calls.append(
+            call("Here are the keyword args:", {"arg1": "Lambda", "arg2": "SnapStart"})
+        )
+        calls.append(call("In function TWO"))
+        calls.append(call("In function ONE"))
+        self.assertEqual(calls, mock_print.mock_calls)