Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions specification/callbacks/recipient_response.yaml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions specification/communications-manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ paths:
post:
$ref: callbacks/channel_status.yaml
tags: ['Callbacks']
/<client-provided-recipient-response-URI>:
post:
$ref: callbacks/recipient_response.yaml
tags: ['Callbacks']
44 changes: 35 additions & 9 deletions specification/documentation/APIDescription.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -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.

Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -203,17 +203,43 @@ 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

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

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.

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.

If a request fails, our retry policy will continue to attempt to deliver the callback for a period of 2 hours.
Speak to our onboarding team if you need to set a rate limit or extend the retry period.

## Two-way messaging

Expand All @@ -225,9 +251,9 @@ 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.

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-/<client-provided-recipient-response-URI>) 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.
Expand All @@ -237,4 +263,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 |
| Text message (SMS) | 918 |
2 changes: 1 addition & 1 deletion specification/documentation/CreateMessage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion specification/documentation/CreateMessageBatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
2 changes: 1 addition & 1 deletion specification/schemas/components/AnswerOptions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion specification/schemas/components/Message.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
88 changes: 88 additions & 0 deletions specification/schemas/components/RecipientResponse.yaml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion specification/schemas/requests/CreateMessage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading