From e5031693b9b96fef70a4a61277d84197203a5ba7 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 10 Jul 2026 11:05:25 +0100 Subject: [PATCH 1/2] update website for two-way messaging and new callbacks --- .../callbacks/recipient_response.yaml | 41 +++++++++ specification/communications-manager.yaml | 4 + specification/documentation/APIDescription.md | 46 ++++++++-- specification/documentation/CreateMessage.md | 2 +- .../documentation/CreateMessageBatch.md | 2 +- .../schemas/components/AnswerOptions.yaml | 2 +- specification/schemas/components/Message.yaml | 2 +- .../schemas/components/RecipientResponse.yaml | 88 +++++++++++++++++++ .../schemas/requests/CreateMessage.yaml | 2 +- 9 files changed, 175 insertions(+), 14 deletions(-) create mode 100644 specification/callbacks/recipient_response.yaml create mode 100644 specification/schemas/components/RecipientResponse.yaml diff --git a/specification/callbacks/recipient_response.yaml b/specification/callbacks/recipient_response.yaml new file mode 100644 index 000000000..75100f66a --- /dev/null +++ b/specification/callbacks/recipient_response.yaml @@ -0,0 +1,41 @@ +summary: Recipient response +description: |- + The recipient response callback sends POST requests to your service with real time updates when a recipient has responded to an NHS App message using a keyword. + + This is an automated way to receive responses from recipients who interact with your messages in the NHS App. + + If you're onboarding with NHS Notify and need to use the recipient response callback, contact the onboarding team to tell them you want to subscribe to recipient responses. + + If you're live with NHS Notify and need to use the recipient response callback, [raise a Service Now ticket](https://nhsdigitallive.service-now.com/csm). + + ### About recipient responses + + When you send a message via the NHS App that includes answer options (keywords), recipients can respond using those keywords. When a response is received, NHS Notify sends a callback to your service with the details of the response. + + The callback includes the keyword code used by the recipient, the channel and message it relates to, and a unique response identifier. + + ### Authentication + + This callback uses mutual TLS (mTLS) for authentication instead of HMAC signatures or API keys. NHS Notify presents a client certificate when connecting to your endpoint. Your service must validate the inbound client certificate against the NHS Notify CA to authenticate the request. +operationId: post-v1-recipient-response-callbacks +security: [] +requestBody: + content: + application/vnd.api+json: + schema: + type: object + properties: + data: + type: array + minItems: 1 + items: + $ref: ../schemas/components/RecipientResponse.yaml +responses: + '202': + description: Accepted + '401': + description: Unauthorized + '403': + description: Forbidden + '429': + $ref: ../responses/4xx/callbacks/429_TooManyRequests_Callbacks.yaml diff --git a/specification/communications-manager.yaml b/specification/communications-manager.yaml index 2fd9157d2..5c2979ad8 100644 --- a/specification/communications-manager.yaml +++ b/specification/communications-manager.yaml @@ -43,3 +43,7 @@ paths: post: $ref: callbacks/channel_status.yaml tags: ['Callbacks'] + /: + post: + $ref: callbacks/recipient_response.yaml + tags: ['Callbacks'] diff --git a/specification/documentation/APIDescription.md b/specification/documentation/APIDescription.md index da80ba90d..9d42c510c 100644 --- a/specification/documentation/APIDescription.md +++ b/specification/documentation/APIDescription.md @@ -54,7 +54,7 @@ The `Accept` header can contain the following values: * `application/json` * `application/vnd.api+json` -The `Accept` header may optionally include a `charset` attribute. If included, it **must** be set to `charset=utf-8` Any other `charset` value will result in a `415` error response. If omitted then `utf-8` is assumed. +The `Accept` header may optionally include a `charset` attribute. If included, it **must** be set to `charset=utf-8` Any other `charset` value will result in a `415` error response. If omitted then `utf-8` is assumed. Where no `Accept` header is present, this will default to `application/vnd.api+json` @@ -65,7 +65,7 @@ This API will accept request payloads of the following types: * `application/vnd.api+json` - see [JSON:API specification](https://jsonapi.org/format/#introduction) * `application/json` -The `Content-Type` header may optionally include a `charset` attribute. If included, it **must** be set to `charset=utf-8` Any other `charset` value will result in a `406` error response. If omitted then `utf-8` is assumed. +The `Content-Type` header may optionally include a `charset` attribute. If included, it **must** be set to `charset=utf-8` Any other `charset` value will result in a `406` error response. If omitted then `utf-8` is assumed. If you attempt to send a payload without the `Content-Type` header set to either of these values then the API will respond with a `415 Unsupported Media Type` response. @@ -153,7 +153,7 @@ You'll then need to include this personalisation field in your request. ### Making your request to send messages from your software Use the following routing plan IDs and personalisation fields that match the message channel your user will send their message with. -| Message channel | First message channel delivery failure time | Second message channel delivery failure time | Personalisation fields | Routing plan ID | +| Message channel | First message channel delivery failure time | Second message channel delivery failure time | Personalisation fields | Routing plan ID | |-------------------------------------------------------|---------------------------------------------|----------------------------------------------|----------------------------------------|--------------------------------------| | NHS App message | 24 hours | - | body | 00000000-0000-0000-0000-000000000001 | | Email | 72 hours | - | email_subject, email_body | 00000000-0000-0000-0000-000000000002 | @@ -203,7 +203,21 @@ You may develop one or many endpoints on your service if you want to receive cal We have created an OpenAPI specification detailing the behaviour of the endpoint that consumers should create to subscribe to callbacks. -We will send your API key in the `x-api-key header`. Your service should respond with: +### Mutual TLS + +**This feature is currently under development and is not yet ready to use.** + +We are currently developing a new callbacks service which uses mutual TLS (mTLS). + +If you are using the newer callbacks mechanism, then your service must check the client certificate which is presented to your service during the callback in order to verify that the response has come from NHS Notify. Your service must reject connection attempts where the client certificate can not be verified by the NHS Notify root CA certificate. + +If the client certificate can not be verified, your service should respond with `403 Forbidden`. + +### HMAC-SHA256 signature checking + +If you are still using the older (original) callbacks mechanism, mTLS is not supported. + +Instead, we will send your API key in the `x-api-key` header. Your service should respond with: * `401 Unauthorized` if the API key is not received * `401 Unauthorized` if the API key is invalid @@ -211,9 +225,21 @@ We will send your API key in the `x-api-key header`. Your service should respond We will send you a HMAC-SHA256 signature in the `x-hmac-sha256-signature` header. You will need to validate the signature to verify the response has come from NHS Notify. This can be achieved by hashing the request body using the HMAC-SHA256 algorithm with a secret value that is comprised of a concatenation of your APIM application ID and the API key that we provide you. The secret takes the following form `[APPLICATION_ID].[API_KEY]`. If you receive a request with an invalid signature you should ignore it and respond with a `403 Forbidden`. -Every request includes an `idempotencyKey` field located in the meta collection of the body. This can help ensure your system remains idempotent, capable of managing duplicate delivery of callbacks. It's important to note that requests may be delivered non-sequentially. +### Deduplication -If a request fails, our retry policy will continue to attempt to deliver the callback for a period of 2 hours. +The NHS Notify callback service attempts to guarantee at-least-once delivery, which may mean that in unusual circumstances the same callback may arrive more than once. + +To help ensure that your system remains idempotent and capable of managing duplicate delivery of callbacks, every request includes an `idempotencyKey` field located in the meta collection of the body. + +It's important to note that requests may be delivered non-sequentially. + +### Retrying and rate limiting + +If a callback fails, our retry policy will continue to attempt to deliver the callback for a period of 2 hours. If you are using the newer callback mechanism, this is extendable on request for a period of up to 12 hours. + +If you are using the newer callback mechanism, it is also possible to limit the rate which NHS Notify will send callbacks. This can be done per-endpoint. + +Speak to our onboarding team if you need to set a rate limit or extend the retry period. ## Two-way messaging @@ -225,9 +251,11 @@ e.g. Question: Can you still attend your GP appointment on Tuesday 26th May at 14:00? Answer options: [Yes, No] -In order to present the recipient with answers include the `answerOptions` field. +In order to present the recipient with answers, include the `answerOptions` field. + +If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message. See [the recipient response callback](#post-/) for more details. -This feature is currently only supported by the NHSAPP channel. +Note: this feature is currently only supported by the `nhsapp` channel. ## Message character limits Different character limits apply to each of the communication channels as listed below. NHS Notify will validate that any personalisation fields submitted in the send message request do not exceed these limits but it is the client's responsibility to ensure that when personalisation is combined with any templated text, the channel character limit is not exceeded. @@ -237,4 +265,4 @@ Different character limits apply to each of the communication channels as listed | Email | 100,000 | | Letter | 15,000 | | NHS App | 5,000 | -| Text message (SMS) | 918 | \ No newline at end of file +| Text message (SMS) | 918 | diff --git a/specification/documentation/CreateMessage.md b/specification/documentation/CreateMessage.md index 8b08e3e0b..3f4dff078 100644 --- a/specification/documentation/CreateMessage.md +++ b/specification/documentation/CreateMessage.md @@ -33,6 +33,6 @@ Here is an example curl request which creates a message using one of these routi curl -X POST \ --header "Accept: */*" \ --header "Content-type: application/vnd.api+json" \ - -d '{"data": {"type": "Message","attributes": {"routingPlanId": "b838b13c-f98c-4def-93f0-515d4e4f4ee1","messageReference": "da0b1495-c7cb-468c-9d81-07dee089d728","recipient": {"nhsNumber": "9990548609"},"originator": {"odsCode":"X123"},"personalisation": {},"answerOptions": {"NHSAPP":[{"code":"Yes"},{"code":"No"}]}}}}' \ + -d '{"data": {"type": "Message","attributes": {"routingPlanId": "b838b13c-f98c-4def-93f0-515d4e4f4ee1","messageReference": "da0b1495-c7cb-468c-9d81-07dee089d728","recipient": {"nhsNumber": "9990548609"},"originator": {"odsCode":"X123"},"personalisation": {},"answerOptions": {"nhsapp":[{"code":"Yes"},{"code":"No"}]}}}}' \ https://sandbox.api.service.nhs.uk/comms/v1/messages ``` diff --git a/specification/documentation/CreateMessageBatch.md b/specification/documentation/CreateMessageBatch.md index 6d1a27f8a..45839b5d7 100644 --- a/specification/documentation/CreateMessageBatch.md +++ b/specification/documentation/CreateMessageBatch.md @@ -38,6 +38,6 @@ Here is an example curl request which creates a message batch using one of these curl -X POST \ --header "Accept: */*" \ --header "Content-type: application/vnd.api+json" \ - -d '{"data": {"type": "MessageBatch","attributes": {"routingPlanId": "b838b13c-f98c-4def-93f0-515d4e4f4ee1","messageBatchReference": "da0b1495-c7cb-468c-9d81-07dee089d728","messages": [{"messageReference": "703b8008-545d-4a04-bb90-1f2946ce1575","recipient": {"nhsNumber": "9990548609"},"originator": {"odsCode":"X123"},"personalisation": {},"answerOptions": {"NHSAPP":[{"code":"Yes"},{"code":"No"}]}}]}}}' \ + -d '{"data": {"type": "MessageBatch","attributes": {"routingPlanId": "b838b13c-f98c-4def-93f0-515d4e4f4ee1","messageBatchReference": "da0b1495-c7cb-468c-9d81-07dee089d728","messages": [{"messageReference": "703b8008-545d-4a04-bb90-1f2946ce1575","recipient": {"nhsNumber": "9990548609"},"originator": {"odsCode":"X123"},"personalisation": {},"answerOptions": {"nhsapp":[{"code":"Yes"},{"code":"No"}]}}]}}}' \ https://sandbox.api.service.nhs.uk/comms/v1/message-batches ``` diff --git a/specification/schemas/components/AnswerOptions.yaml b/specification/schemas/components/AnswerOptions.yaml index 5c1cc2d30..eb2c8c912 100644 --- a/specification/schemas/components/AnswerOptions.yaml +++ b/specification/schemas/components/AnswerOptions.yaml @@ -7,7 +7,7 @@ description: |- This feature needs to be enabled on your account and is subject to the relevant callback integration being completed. properties: - NHSAPP: + nhsapp: type: array minItems: 1 maxItems: 6 diff --git a/specification/schemas/components/Message.yaml b/specification/schemas/components/Message.yaml index f2f375f73..ab3575bef 100644 --- a/specification/schemas/components/Message.yaml +++ b/specification/schemas/components/Message.yaml @@ -17,7 +17,7 @@ properties: description: The personalisation keys and values for this message. These are linked to the routingPlanId provided and are agreed upon during [onboarding](#overview--onboarding). answerOptions: example: - NHSAPP: + nhsapp: - code: Yes - code: No $ref: ./AnswerOptions.yaml diff --git a/specification/schemas/components/RecipientResponse.yaml b/specification/schemas/components/RecipientResponse.yaml new file mode 100644 index 000000000..ea1ea49ed --- /dev/null +++ b/specification/schemas/components/RecipientResponse.yaml @@ -0,0 +1,88 @@ +type: object +title: CallbackRequest +required: + - type + - attributes + - links + - meta +properties: + type: + type: string + example: "RecipientResponse" + enum: + - RecipientResponse + attributes: + type: object + properties: + messageId: + $ref: ../types/KSUID.yaml + messageReference: + $ref: ../../snippets/MessageReferenceSnippet.yaml + cascadeType: + $ref: ../enums/CascadeType.yaml + channel: + type: string + enum: + - nhsapp + example: nhsapp + channelStatus: + type: string + description: The status of the channel at the time the response was received. + enum: + - sending + - delivered + - failed + example: delivered + channelFailureReasonCode: + type: string + description: If there is a failed reason code associated with this channel, it is provided here. + example: "CFR_NHSAPP_0001" + code: + type: string + description: The keyword code from the recipient's response. + example: "YES" + responseId: + type: string + description: The unique identifier for the recipient response. + format: uuid + example: "33333333-3333-4333-8333-333333333333" + authoredAt: + type: string + description: The date-time the recipient authored the response. + format: date-time + example: '2026-01-15T10:30:00Z' + timestamp: + type: string + description: Date-time for when the response was processed. + format: date-time + example: '2026-01-15T10:30:05Z' + required: + - messageId + - messageReference + - cascadeType + - channel + - channelStatus + - code + - responseId + - authoredAt + - timestamp + links: + type: object + properties: + recipientResponse: + type: string + description: URL to retrieve the recipient response details. + format: uri + example: "https://api.service.nhs.uk/comms/v1/messages/2WL3qFTEFM0qMY8xjRbt1LIKCzM/recipient-response/33333333-3333-4333-8333-333333333333" + required: + - recipientResponse + meta: + type: object + properties: + idempotencyKey: + type: string + description: Contains a value that you can use to deduplicate retried + requests. + example: "2515ae6b3a08339fba3534f3b17cd57cd573c57d25b25b9aae08e42dc9f0a445" + required: + - idempotencyKey diff --git a/specification/schemas/requests/CreateMessage.yaml b/specification/schemas/requests/CreateMessage.yaml index 47aa862e7..4190fa26d 100644 --- a/specification/schemas/requests/CreateMessage.yaml +++ b/specification/schemas/requests/CreateMessage.yaml @@ -41,7 +41,7 @@ properties: description: "The personalisation keys and values for this message. These are linked to the routingPlanId provided and are agreed upon during [onboarding](#overview--onboarding)." answerOptions: example: - NHSAPP: + nhsapp: - code: Yes - code: No $ref: ../components/AnswerOptions.yaml From 46a839b83d55e06f017961b2315a933e6ca2dee9 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 10 Jul 2026 16:04:39 +0100 Subject: [PATCH 2/2] woring adjustments --- specification/documentation/APIDescription.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/specification/documentation/APIDescription.md b/specification/documentation/APIDescription.md index 9d42c510c..b103572a0 100644 --- a/specification/documentation/APIDescription.md +++ b/specification/documentation/APIDescription.md @@ -227,7 +227,7 @@ This can be achieved by hashing the request body using the HMAC-SHA256 algorithm ### Deduplication -The NHS Notify callback service attempts to guarantee at-least-once delivery, which may mean that in unusual circumstances the same callback may arrive more than once. +The NHS Notify callback service attempts to guarantee at-least-once delivery, which means that sometimes the same callback may arrive more than once. To help ensure that your system remains idempotent and capable of managing duplicate delivery of callbacks, every request includes an `idempotencyKey` field located in the meta collection of the body. @@ -253,9 +253,7 @@ Answer options: [Yes, No] In order to present the recipient with answers, include the `answerOptions` field. -If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message. See [the recipient response callback](#post-/) for more details. - -Note: this feature is currently only supported by the `nhsapp` channel. +If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message (currently only NHS App supports this). See [the recipient response callback](#post-/) for more details. ## Message character limits Different character limits apply to each of the communication channels as listed below. NHS Notify will validate that any personalisation fields submitted in the send message request do not exceed these limits but it is the client's responsibility to ensure that when personalisation is combined with any templated text, the channel character limit is not exceeded.