What are SOAP and WSDL?
SOAP is an XML messaging protocol: every message is an XML envelope with an optional header and a mandatory body, usually sent as an HTTP POST. WSDL is the XML language that describes a SOAP service: its operations, the XML schemas of their messages, and the address and binding to call them on. Together they were the dominant way to build web services in the 2000s, and they are still what you meet in banking, insurance, government, telecoms, payment networks and enterprise software.
Nobody should design a new public API on SOAP today; for the alternatives see choosing an API style. This page is for the other case, which is common: you have to integrate with a service that already speaks it. The practical advice is to let tooling do the work. Get the WSDL, generate a client from it, find out which SOAP version and security profile the service actually uses, and spend your effort on the faults and the edge cases rather than on hand-building XML.
1. Versions
| Specification | Status | Date |
|---|---|---|
| SOAP 1.1 | W3C Note (a submission, never a standard) | 8 May 2000 |
| SOAP 1.2 | W3C Recommendation, Second Edition | 27 April 2007 (first edition 2003) |
| WSDL 1.1 | W3C Note | 15 March 2001 |
| WSDL 2.0 | W3C Recommendation | 26 June 2007 |
| WS-I Basic Profile 1.1 | Final interoperability profile | 10 April 2006 |
The formal status is misleading. SOAP 1.1 and WSDL 1.1, the two that were never standards, are by far the most widely deployed. WSDL 2.0 saw little adoption. In practice you will meet SOAP 1.1 or 1.2 described by WSDL 1.1, and the WS-I Basic Profile is what made the 1.1 pair interoperable.
You can tell the version from the envelope namespace:
| Version | Envelope namespace |
|---|---|
| SOAP 1.1 | http://schemas.xmlsoap.org/soap/envelope/ |
| SOAP 1.2 | http://www.w3.org/2003/05/soap-envelope |
A message in the wrong namespace earns a VersionMismatch fault.
2. The envelope over HTTP
The two versions differ most visibly in their HTTP headers.
SOAP 1.1 uses text/xml and a separate SOAPAction header, which the client must send. WS-I Basic Profile requires its value to be a quoted string; an empty string "" means the request URI identifies the intent.
POST /StockService HTTP/1.1Host: legacy.example.comContent-Type: text/xml; charset=utf-8SOAPAction: "urn:example:GetQuote"
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header/> <soap:Body> <m:GetQuote xmlns:m="urn:example:stock"> <m:symbol>ACME</m:symbol> </m:GetQuote> </soap:Body></soap:Envelope>SOAP 1.2 uses application/soap+xml (registered by RFC 3902), and the action moves into an optional action parameter of the Content-Type:
POST /StockService HTTP/1.1Host: legacy.example.comContent-Type: application/soap+xml; charset=utf-8; action="urn:example:GetQuote"
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Body> <m:GetQuote xmlns:m="urn:example:stock"> <m:symbol>ACME</m:symbol> </m:GetQuote> </env:Body></env:Envelope>Getting the action wrong is the commonest first failure. Many servers route on it, and a missing or unquoted SOAPAction produces a fault that says nothing about the real cause. Copy the value from the soapAction attribute in the WSDL binding exactly.
Header blocks marked mustUnderstand="true" (or "1" in SOAP 1.1) must be processed by the receiver or rejected with a MustUnderstand fault, never silently ignored.
3. Faults
Errors come back as a Fault element in the body. The structure changed between versions:
| SOAP 1.1 | SOAP 1.2 | Meaning |
|---|---|---|
faultcode |
Code/Value (with optional nested Subcode) |
Machine-readable code |
faultstring |
Reason/Text (one per xml:lang) |
Human-readable explanation |
faultactor |
Node, Role |
Which node failed |
detail |
Detail |
Application-specific error data |
| SOAP 1.1 code | SOAP 1.2 code | HTTP status (1.1) | HTTP status (1.2) |
|---|---|---|---|
Client |
Sender |
500 |
400 |
Server |
Receiver |
500 |
500 |
VersionMismatch |
VersionMismatch |
500 |
500 |
MustUnderstand |
MustUnderstand |
500 |
500 |
DataEncodingUnknown |
500 |
SOAP 1.1 requires every fault to be a 500 Internal Server Error, including the ones that are the client’s fault. This has a practical consequence: do not treat a 500 from a SOAP service as an outage until you have parsed the body. Most HTTP clients and retry middleware assume 500 means “retry later”; with SOAP 1.1 it often means “your request is wrong and retrying will never help”. Client / Sender faults should not be resent unchanged. Server / Receiver faults may succeed later.
The application-level error you care about is almost always in detail, as a typed element declared in the WSDL. Generated clients usually raise it as a typed exception; catch those, not the generic fault.
4. WSDL
A WSDL 1.1 document has these parts:
| Element | Describes |
|---|---|
types |
XML Schema for the messages |
message |
The parts of each input, output and fault message |
portType |
The abstract operations and their messages |
binding |
How a portType maps to SOAP: style, use, soapAction |
service / port |
The endpoint address for a binding |
WSDL 2.0 renames portType to interface and port to endpoint, drops message in favor of schema elements directly, and can also describe plain HTTP bindings. If you are handed a 2.0 document, check your toolkit supports it before anything else; many do not.
Style and use
A binding’s style is rpc or document (the default), and each body’s use is literal or encoded. The combination that won is document/literal, usually in the “wrapped” convention, where the body holds a single element named after the operation. WS-I Basic Profile requires literal and prohibits SOAP encoding. If you meet rpc/encoded, expect your modern toolkit to refuse it or need a legacy mode.
5. Security
Transport security is TLS, often with client certificates; see mutual TLS. Message-level security is WS-Security (OASIS, current version 1.1.1, 2012), which adds a wsse:Security header block carrying tokens, XML signatures and encrypted elements. The UsernameToken and X.509 token profiles are the forms you are most likely to meet.
WS-Security is where hand-rolled integrations go wrong: XML signatures depend on canonicalization, and a signature over an element that your library reformats will not verify. Use a toolkit with real WS-Security support, and get a known-good signed sample request from the provider to test against.
Best Practices
- Generate the client from the WSDL rather than templating XML strings. Keep the WSDL in your repository so a provider-side change shows up as a diff.
- Explore a new service with a dedicated tool such as SoapUI before writing code; it builds sample requests from the WSDL.
- Parse every
500for a fault before deciding whether to retry. - Log the raw request and response envelopes, with secrets redacted, during integration. A fault with an empty
faultstringis only diagnosable from the wire. - Do not wrap a SOAP service by passing its XML straight through a new REST API. Translate it into your own resources and errors at the boundary, so the legacy shape stays in one place.