NHS Spine
ebMS 2.0 over mutual TLS with NHS party identifiers and CPA ids - England's health messaging backbone.
The Spine is the messaging backbone of health and care systems in England. Its asynchronous exchanges run over ebXML Message Service (ebMS 2.0) - each message a SOAP 1.1 envelope whose header names the sending and receiving parties, the service and the action, with the clinical payloads travelling as MIME parts alongside.
What the Spine mandates
| Requirement | Where it comes from | The Zato block |
|---|---|---|
| ebMS 2.0 message structure | The Spine's External Interface Specification | ebXML messaging over an outgoing SOAP connection |
| NHS party identifiers with a type attribute | The Spine's addressing conventions | from_party_type and to_party_type on the call |
| CPA ids agreed during onboarding | The Spine directory | cpa_id on the call |
| Mutual TLS on the health and social care network | Spine connection agreements | The connection's Client certificate and Client key |
Piecing it together
Step 1. Create the outgoing connection under Connections > Outgoing > SOAP with the message handling service's address. ebMS runs over SOAP 1.1, which is the connection's default version.
Step 2. In the Security tab, set the client certificate and key paths issued for your endpoint - the Spine authenticates message handlers at the TLS layer:

Step 3. The exchange itself is one call - the platform builds the eb:MessageHeader with a fresh message id and timestamp, the eb:Manifest, and the MIME package:
info = EbXMLInfo()
info.from_party = 'my-party-id'
info.from_party_type = 'urn:nhs:names:partyType:ocs+serviceInstance'
info.to_party = 'spine-party-id'
info.to_party_type = 'urn:nhs:names:partyType:ocs+serviceInstance'
info.cpa_id = 'S2001919A2011852'
info.conversation_id = self.cid
info.service = 'urn:nhs:names:services:pdsquery'
info.action = 'QUPA_IN000006UK02'
part = Part()
part.content_id = 'payload-1'
part.content_type = 'application/xml'
part.data = hl7v3_query
ack = self.soap['Spine MHS'].invoke_ebxml(info, [part])
The reply's parsed header comes back with ref_to_message_id pointing at the message it acknowledges - the ebXML page covers the whole call in detail.