Local Options
Local options are passed to the client’s #call method and are specific to a single request.
HTTP
soap_action
You might need to set this if you don’t have a WSDL. Otherwise, Savon should set the proper SOAPAction HTTP header for you. If it doesn’t, please open an issue and add the WSDL of your service.
client.call(:authenticate, soap_action: "urn:Authenticate")
cookies
Savon 2.0 tried to automatically handle cookies by storing the cookies from the last response and using them for the next request. This is wrong and it caused problems. Savon 2.1 does not set the “Cookie” header for you, but it makes it easy for you to handle cookies yourself.
response = client.call(:authenticate, message: credentials)
auth_cookies = response.http.cookies
client.call(:find_user, message: { id: 3 }, cookies: auth_cookies)
The accepted shapes depend on the transport. With the default HTTPI transport, this option
accepts an Array of HTTPI::Cookie objects or any object that responds to cookies
(like for example, an HTTPI::Response). With transport: :faraday, it accepts a String
(used verbatim as the Cookie header) or a Hash of cookie names to values (formatted as
"name=value; name=value"). Either way, response.http.cookies returns a shape you can
pass straight back into this option.
headers
Per-request HTTP headers. Merged with the global headers option, so you can keep a base set on the client and add or override just what changes for this call.
client.call(:find_user,
message: { id: 42 },
headers: { "X-Request-Id" => SecureRandom.uuid }
)
Request
message
You probably want to add some arguments to your request. For simple XML which can easily be represented as a Hash, you can pass the SOAP message as a Hash. Savon uses Gyoku to translate the Hash into XML.
client.call(:authenticate, message: { username: 'luke', password: 'secret' })
For more complex XML structures, you can pass any other object that is not a Hash and responds
to #to_s if you want to use a more specific tool to build your request.
class ServiceRequest
def to_s
builder = Builder::XmlMarkup.new
builder.instruct!(:xml, encoding: "UTF-8")
builder.person { |b|
b.username("luke")
b.password("secret")
}
builder
end
end
client.call(:authenticate, message: ServiceRequest.new)
message_tag
You can change the name of the SOAP message tag. If you need to use this option, please open an issue let me know why.
client.call(:authenticate, message_tag: :authenticationRequest)
This should be set by Savon if it has a WSDL. If it doesn’t, it generates a message tag from the SOAP
operation name. When the WSDL defines typed message parts for the operation, the WSDL’s tag is
always used and message_tag is ignored. Here’s how the option changes the request.
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://v1.example.com/">
<env:Body>
<tns:authenticationRequest>
</tns:authenticationRequest>
</env:Body>
</env:Envelope>
attributes
The attributes option accepts a Hash of XML attributes for the SOAP message tag.
client.call(:authenticate, :attributes => { "ID" => "ABC321" })
Here’s what the request will look like.
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://v1.example.com/">
<env:Body>
<tns:authenticationRequest ID="ABC321">
</tns:authenticationRequest>
</env:Body>
</env:Envelope>
If you need to use this option, please open an issue and provide you WSDL for debugging. This should be handled automatically, but we need real world examples to do so.
soap_header
Since v2.3.0 you can specify the SOAP header per request. When both the global and local option is used, Savon will merge the global with the local Hash and the local keys win.
client.call(:authenticate, :soap_header => { "OpToken" => "secret" })
xml
If you need to, you can even shortcut Savon’s Builder and send your very own XML.
client.call(:authenticate, xml: "<envelope><body></body></envelope>")
This skips envelope, header, WSSE, and message-tag building entirely — the XML is sent as-is
(still wrapped in a multipart request when attachments are present).
attachments
Send SOAP-with-Attachments parts alongside the SOAP envelope. When attachments
is present, Savon sends the request as multipart/related. The SOAP envelope is
the root MIME part and each attachment is added as a separate part.
Savon does not add attachment references to the SOAP body automatically. Build the
SOAP message so it references the attachment Content-ID expected by your service,
usually with a cid: URL.
Pass an Array of hashes with :filename and :content. The filename becomes the
attachment Content-ID and Content-Location:
client.call(:upload,
message: {
document: "",
:attributes! => { document: { href: "cid:report.xml" } }
},
attachments: [
{ filename: "report.xml", content: "<xml>...</xml>" }
]
)
The multipart body will contain a SOAP root part and an attachment part similar to this, with boundaries and some Mail-generated headers omitted for clarity:
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 7bit
Content-ID: <soap-request-body@soap>
<env:Envelope ...>
<env:Body>
<tns:upload>
<document href="cid:report.xml"></document>
</tns:upload>
</env:Body>
</env:Envelope>
--boundary
Content-Type: application/xml; filename=report.xml
Content-Transfer-Encoding: base64
Content-Location: report.xml
Content-ID: <report.xml>
PHhtbD4uLi48L3htbD4=
Pass a Hash of "content-id" => path to read attachments from disk. The Hash key
becomes the attachment Content-ID, independent of the file basename:
client.call(:upload,
message: {
document: "",
:attributes! => { document: { href: "cid:invoice" } }
},
attachments: {
"invoice" => "/tmp/invoice-2026-05.pdf"
}
)
Pass an Array of paths to use each file’s basename as the attachment Content-ID:
client.call(:upload,
message: { user_id: 42 },
attachments: [
"/tmp/report.xml",
"/tmp/scan.pdf"
]
)
Those parts can be referenced from the SOAP body as cid:report.xml and cid:scan.pdf.
This feature sends generic SOAP-with-Attachments MIME parts. Savon does not create
MTOM/XOP xop:Include elements, optimize base64 content, or validate those references.
If your service requires a specific element name, namespace, or attribute for attachment
references, build that XML in the message or xml option and make sure its cid:
value matches the attachment Content-ID. To switch the request to MTOM
application/xop+xml framing instead, pass mtom: true (see below).
mtom
Send the request with MTOM/XOP framing instead of plain SOAP-with-Attachments. The envelope
becomes the application/xop+xml root part of a multipart/related request, and the binary
parts travel as MIME attachments instead of base64 text. You build the xop:Include XML
yourself, and each attachment’s Content-ID must match the cid: href:
client.call(:upload,
xml: <<~XML,
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xop="http://www.w3.org/2004/08/xop/include">
<soap:Body>
<tns:upload><tns:file><xop:Include href="cid:doc1@example"/></tns:file></tns:upload>
</soap:Body>
</soap:Envelope>
XML
mtom: true,
attachments: [{ filename: "report.pdf", content_id: "doc1@example", content: File.binread("report.pdf") }]
)
mtom: true without attachments has no effect. Savon does not rewrite base64 XML into XOP,
generate xop:Include nodes, infer MIME types from the XML, or deserialize inbound MTOM
responses. For SOAP 1.2, the action is still sent as a SOAPAction header, as elsewhere in
Savon, rather than as the action= parameter of the start-info media type.
Authentication
The three WSSE options accepted by the client constructor can also be
passed per request. The local value overrides the global for that single call. Use this when
one operation needs different credentials, a different signing key, or needs to disable a
header that’s normally set globally. Only wsse_auth and wsse_timestamp can be disabled
per call — see below.
wsse_auth
Per-request WSSE basic or digest credentials. Overrides the global wsse_auth for this call only.
client.call(:authenticate, wsse_auth: ["lea", "top-secret", :digest])
Pass false to suppress a globally configured wsse_auth for one call:
client.call(:public_lookup, wsse_auth: false)
wsse_timestamp
Per-request WS-Security timestamp. Overrides the global wsse_timestamp for this call.
client.call(:authenticate, wsse_timestamp: true)
wsse_signature
Per-request XML Signature via Akami. Overrides the global
wsse_signature for this call. Passing false or nil does not disable a global signature —
it falls back to it. There is no per-call way to suppress signing.
signature = Akami::WSSE::Signature.new(
Akami::WSSE::Certs.new(
cert_file: "client_cert.pem",
private_key_file: "client_key.pem"
)
)
client.call(:sign_document, wsse_signature: signature)
Response
advanced_typecasting
Savon by default tells Nori to use its “advanced typecasting” to convert XML values like
"true" to TrueClass, dates to date objects, etc.
client.call(:authenticate, advanced_typecasting: false)
response_parser
Savon defaults to Nori’s Nokogiri parser. Nori ships with a REXML parser as an alternative. If you need to switch to REXML, please open an issue and describe the problem you have with the Nokogiri parser.
client.call(:authenticate, response_parser: :rexml)
multipart
deprecated
No-op since v2.13.0. Savon detects multipart (MTOM) responses from the Content-Type
header and parses them regardless of this option, so it never enabled or disabled
anything for the call. Safe to remove. When the response is multipart, response.attachments
exposes the parts attached to the response.