Post

SOAP: XML Web Services, WSDL and WS-Security

What SOAP is, why Microsoft and partners created it, the envelope, WSDL contracts, faults and WS-* standards, how a SOAP call flows, and where it still runs in banking, payments and government.

SOAP: XML Web Services, WSDL and WS-Security

SOAP is an XML-based messaging protocol for calling operations on remote services. Every message is an XML Envelope with an optional Header and a Body, and every service publishes a formal contract in WSDL from which clients are generated. SOAP dominated enterprise integration in the 2000s and, although new public APIs are rarely SOAP today, it still runs a large share of banking, insurance, telecom, healthcare and government systems.

This post is part of the API communication protocols series.

At a glance

 SOAP
TypeMessaging protocol (W3C Recommendation)
Created1998 by Dave Winer, Don Box, Bob Atkinson and Mohsen Al-Ghosein at Microsoft; SOAP 1.1 W3C Note in 2000; SOAP 1.2 Recommendation in 2003
NameOriginally “Simple Object Access Protocol”; since 1.2 just SOAP
TransportUsually HTTP POST; also SMTP, JMS, TCP
PayloadXML only, validated by XML Schema (XSD)
ContractRequired: WSDL (Web Services Description Language)
ErrorsStandard Fault element
ExtensionsWS-Security, WS-Addressing, WS-ReliableMessaging, WS-AtomicTransaction, WS-Policy
Best forIntegrations that require formal contracts, message-level security and existing WSDLs

What SOAP is

A SOAP message is a document, not a URL plus a verb. The operation and its parameters are inside the XML body, and everything goes to one endpoint URL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /AccountService HTTP/1.1
Host: bank.example.com
Content-Type: application/soap+xml; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
               xmlns:acc="http://bank.example.com/accounts">
  <soap:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <!-- signed timestamp, binary security token, signature -->
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <acc:GetAccountBalance>
      <acc:AccountId>DE89370400440532013000</acc:AccountId>
    </acc:GetAccountBalance>
  </soap:Body>
</soap:Envelope>

The response is another envelope:

1
2
3
4
5
6
7
8
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
               xmlns:acc="http://bank.example.com/accounts">
  <soap:Body>
    <acc:GetAccountBalanceResponse>
      <acc:Balance currency="EUR">1520.75</acc:Balance>
    </acc:GetAccountBalanceResponse>
  </soap:Body>
</soap:Envelope>
PartPurpose
EnvelopeRoot element; its namespace identifies the SOAP version (1.1 or 1.2)
HeaderOptional metadata processed by intermediaries: security, routing, transactions, correlation
BodyThe operation and its parameters, or a Fault
FaultStandard error structure: Code (Sender, Receiver, VersionMismatch, MustUnderstand), Reason, Detail

WSDL: the contract

A WSDL document describes what a service offers and how to reach it. WSDL 1.1 has five main parts:

ElementDescribes
typesXML Schema definitions of the messages’ data
messageThe input and output messages
portTypeThe abstract interface: a set of operations
bindingHow the operations map to SOAP over HTTP (style, encoding)
service / portThe concrete endpoint address

Tools such as wsimport (Java), svcutil and dotnet-svcutil (.NET), Apache CXF and Python’s zeep read a WSDL and generate typed proxy classes, so calling a SOAP service looks like calling a local method.

The relationships between WSDL elements as a UML class diagram:

classDiagram
    class Service {
        name
    }
    class Port {
        address location
    }
    class Binding {
        transport HTTP
        style document
    }
    class PortType {
        name
    }
    class Operation {
        name
    }
    class Message {
        name
    }
    class XsdType {
        complexType
    }
    Service "1" *-- "1..*" Port
    Port --> Binding : uses
    Binding --> PortType : implements
    PortType "1" *-- "1..*" Operation
    Operation --> Message : input
    Operation --> Message : output
    Operation --> Message : fault
    Message --> XsdType : parts typed by

Why it was developed

In the late 1990s, distributed objects were built with DCOM (Microsoft), CORBA (OMG) and Java RMI. Each was tied to a platform or required complex brokers, and their binary protocols on custom ports were blocked by corporate firewalls. XML and HTTP had just become universal, so Dave Winer and a Microsoft team designed XML-RPC (1998) and then SOAP: send an XML document over HTTP port 80, and any platform that can parse XML can call any other.

IBM, Microsoft and others then built the WS-* stack on top of SOAP for enterprise needs: message signing and encryption (WS-Security, an OASIS standard from 2004), reliable delivery, distributed transactions and policies. SOAP became the backbone of Service-Oriented Architecture (SOA) and Enterprise Service Buses. Its complexity is also why developers moved to REST in the late 2000s.

How it works

A SOAP call goes through a generated proxy, the SOAP endpoint and header processing. Switch on the fault mode to see how errors are returned:

  1. Call a typed method: The application calls GetAccountBalance(accountId) on a proxy class that a tool generated from the service's WSDL.
  2. Build the envelope: The proxy serialises the call into an XML Envelope with a Header (security token, addressing) and a Body that matches the XSD types.
  3. POST to the endpoint: SOAP 1.2 posts to one URL with Content-Type: application/soap+xml. The HTTP verb carries no meaning; the operation is in the Body.
  4. Check the header: The server validates the XML against the schema and checks the WS-Security signature, timestamp and token in the Header.
  5. Run the operation (ok): The Body is mapped to the GetAccountBalance operation defined in the WSDL portType and executed.
  6. Response envelope (ok): The result comes back as another Envelope. The proxy deserialises it into a typed object for the caller.
  7. Raise a fault (fault): The signature is invalid, so the server builds a standard Fault element with a code (Client or Sender), a reason and optional detail.
  8. Fault reaches the client (fault): The Fault travels back with HTTP 500. Generated proxies turn it into a typed exception, so error handling is part of the contract.
sequenceDiagram
    autonumber
    participant A as Client app
    participant P as Generated proxy
    participant E as SOAP endpoint
    participant S as Service operation
    A->>P: GetAccountBalance(accountId)
    P->>P: serialise to XML Envelope, sign Header
    P->>E: HTTP POST, application/soap+xml
    E->>E: validate XSD and WS-Security
    alt valid
        E->>S: invoke operation
        S-->>E: result
        E-->>P: 200 OK, response Envelope
        P-->>A: typed result
    else invalid signature
        E-->>P: 500, Fault with Code Sender
        P-->>A: throws typed exception
    end

SOAP 1.1 versus 1.2

 SOAP 1.1SOAP 1.2
Content typetext/xml plus a SOAPAction headerapplication/soap+xml with an optional action parameter
Envelope namespacehttp://schemas.xmlsoap.org/soap/envelope/http://www.w3.org/2003/05/soap-envelope
Fault codesClient, ServerSender, Receiver
StatusW3C NoteW3C Recommendation

Styles and attachments

Use document/literal wrapped style: the Body contains one element defined by an XML Schema, which is what the WS-I Basic Profile recommends for interoperability. The older RPC/encoded style is deprecated. Binary data travels as Base64 in the XML or, more efficiently, as MTOM attachments.

Where it is used

  • Banking and payments: core banking interfaces, card processors, interbank messaging gateways.
  • Insurance and healthcare: policy administration systems; parts of HL7 v3 and IHE profiles.
  • Telecom: provisioning and billing systems built in the 2000s.
  • Government and tax: filing and e-invoicing gateways in several countries.
  • Enterprise software: older SAP, Oracle, Microsoft Dynamics and Salesforce integration APIs.
  • Logistics: carrier shipping, rating and tracking APIs.

Real-life examples

EU VIES VAT number validation

The European Commission’s VIES service, which checks whether an EU VAT number is valid, has long been offered as a SOAP web service with a public WSDL (checkVatService.wsdl). E-commerce platforms and invoicing systems call its checkVat operation with a country code and a number before applying reverse-charge VAT on cross-border B2B invoices. Libraries in every language wrap that one WSDL, which shows SOAP’s main strength: a published contract that clients can be generated from without any hand-written documentation.

Salesforce and carrier APIs

Salesforce still offers a SOAP API next to its REST API. Customers download an Enterprise WSDL (strongly typed to their own org’s objects) or a Partner WSDL (generic), generate a client in Java or .NET, and use it for back-office integrations. Parcel carriers have a similar history: UPS, FedEx and national postal services exposed rating, shipping and tracking as SOAP web services for years, and logistics systems built against those WSDLs are still common even as the carriers move to REST.

Strengths and limitations

StrengthsLimitations
Formal, machine-readable contract and generated clientsVerbose XML; larger and slower than JSON or Protobuf
Message-level security (signing, encryption) independent of transportComplex: WS-* standards, namespaces, interoperability profiles
Standard error model (Fault)Uses HTTP as a tunnel: always POST, no HTTP caching
Reliable messaging and distributed transactions via WS-*Poor fit for browsers, mobile and modern tooling
Transport independent (HTTP, JMS, SMTP)XML parsing attacks (XXE, billion laughs) if parsers are not hardened

Best practices

  • Harden XML parsers: disable external entities and DTDs to prevent XXE, and limit document size and depth.
  • Use document/literal wrapped and follow the WS-I Basic Profile.
  • Combine TLS with WS-Security where messages pass through intermediaries.
  • Version namespaces (.../accounts/v2) rather than changing existing types.
  • Wrap legacy SOAP services with a REST or GraphQL facade (API gateway or BFF) for new clients, instead of spreading SOAP to new code.
  • Log and monitor Faults, not only HTTP status codes.

When to use SOAP, and when not

Use SOAP when you integrate with a system that only offers a WSDL, when a regulator or partner mandates it, or when you need message-level signatures and WS-* features that the parties already support.

For new APIs, prefer REST for public interfaces and gRPC for typed internal contracts: they give most of SOAP’s benefits with far less weight.

This post is licensed under CC BY 4.0 by the author.