diff --git a/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml b/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml
new file mode 100644
index 00000000..829b9f2a
--- /dev/null
+++ b/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml b/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml
new file mode 100644
index 00000000..160b92df
--- /dev/null
+++ b/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/proxies/live/apiproxy/resources/py/mirror-request-headers.py b/proxies/live/apiproxy/resources/py/mirror-request-headers.py
new file mode 100644
index 00000000..8ee52123
--- /dev/null
+++ b/proxies/live/apiproxy/resources/py/mirror-request-headers.py
@@ -0,0 +1,10 @@
+translated_header_names = ["X-Request-ID", "X-Correlation-ID"]
+for translated_header_name in translated_header_names:
+ # Access original request headers dictionary
+ original_request_header = flow.getVariable("original_request_header." + translated_header_name)
+ if original_request_header:
+ original_request_header = original_request_header.split("=")
+ # Remove original response header
+ flow.removeVariable("response.header." + translated_header_name)
+ # Mirror original request header
+ flow.setVariable("response.header." + original_request_header[0], original_request_header[1])
diff --git a/proxies/live/apiproxy/resources/py/translate-request-headers.py b/proxies/live/apiproxy/resources/py/translate-request-headers.py
new file mode 100644
index 00000000..61d8094e
--- /dev/null
+++ b/proxies/live/apiproxy/resources/py/translate-request-headers.py
@@ -0,0 +1,27 @@
+def get_request_headers():
+ request_header_names = flow.getVariable("request.headers.names")
+ request_headers = {}
+ for name in request_header_names:
+ request_headers[name] = flow.getVariable("request.header." + name)
+
+ return request_headers
+
+
+# Access request headers dictionary
+request_headers = get_request_headers()
+
+# Map of lowercase header name to desired parcel case header name
+request_header_translation = {"x-request-id": "X-Request-ID", "x-correlation-id": "X-Correlation-ID"}
+
+# Loop through request headers
+for original_header_name, header_value in request_headers.items():
+ desired_header_name = request_header_translation.get(original_header_name.lower())
+ if desired_header_name:
+ # Store original header against the desired name for mirroring in response
+ flow.setVariable(
+ "original_request_header." + desired_header_name, "%s=%s" % (original_header_name, header_value)
+ )
+ # Remove original request header
+ flow.removeVariable("request.header." + original_header_name)
+ # Set header with correct casing
+ flow.setVariable("request.header." + desired_header_name, header_value)
diff --git a/proxies/live/apiproxy/targets/target.xml b/proxies/live/apiproxy/targets/target.xml
index f779224d..37dce55c 100644
--- a/proxies/live/apiproxy/targets/target.xml
+++ b/proxies/live/apiproxy/targets/target.xml
@@ -8,6 +8,9 @@
FlowCallout.ApplyRateLimiting
+
+ TranslateRequestHeaders
+
AddDeveloperAppData
@@ -45,7 +48,7 @@
-
+
accesstoken.auth_type = "user"
@@ -64,28 +67,44 @@
-
SetMimeType
+
+ MirrorRequestHeaders
+
+
+
+ MirrorRequestHeaders
+
+
+
+ MirrorRequestHeaders
+
RaiseFault.401Unauthorized
oauthV2.OauthV2.VerifyAccessToken.failed = true or fault.name = "invalid_access_token" or fault.name = "InvalidAccessToken" or fault.name = "access_token_not_approved" or fault.name = "apiresource_doesnot_exist" or fault.name = "InvalidAPICallAsNo" or fault.name = "ApiProductMatchFound" or fault.name = "access_token_expired"
+
+ MirrorRequestHeaders
+
RaiseFault.404NotFound
response.header.x-amzn-ErrorType = "IncompleteSignatureException"
+
+ MirrorRequestHeaders
+
RaiseFault.ApplicationOperationOutcome
response.header.x-amzn-RequestId != null
@@ -101,4 +120,4 @@
User-Agent,Referer,Accept-Language
-
+
\ No newline at end of file
diff --git a/specification/validated-relationships-service-api.yaml b/specification/validated-relationships-service-api.yaml
index a06da7e3..8dab3159 100644
--- a/specification/validated-relationships-service-api.yaml
+++ b/specification/validated-relationships-service-api.yaml
@@ -87,6 +87,9 @@ info:
|[NHS login - combined authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-combined-authentication-and-authorisation) |OAuth 2.0 authorisation code with API key and secret |No need to integrate and onboard separately with NHS login. |No access to user information. |
|[NHS login - separate authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-separate-authentication-and-authorisation) |OAuth 2.0 token exchange with signed JWT |Gives access to user information. |Need to integrate and onboard separately with NHS login. |
+ ## Headers
+ This API is case-insensitive when processing request headers, meaning it will accept headers regardless of the letter casing used. (e.g. X-Request-Id, x-request-id are treated the same). When sending headers back in the response, we preserve the exact casing as received in the original request.
+
## Errors
We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range: