Add an Encoded Attachment to a cXML Invoice

Learn how to transmit invoice attachments as part of the invoice cXML.

Overview

You can transmit invoice attachments as part of the invoice cXML using Base64 multipart. You'll need to build your own script that takes three inputs and encodes the attachments and image scans (but not cXML) in Base64 multipart format. Your script will also need to create a random boundary, shown in the example encoded invoice, below. It also needs to include Content-Type and Content-ID for each file.

Script inputs

Note

When handling multipart file uploads, it's necessary to ensure that the correct newline character sequence is used. You must set the newline character as CRLF (\r\n) for parsing multipart boundaries. The incorrect formatting may result in a 500 internal server error.

For more information, see Multipart requests for Windows and Unix based systems.

To generate a cXML Invoice with attachments encoded in it, you need to create a script that takes specific inputs and generates a cXML invoice with the encoded attachments.

Make sure that your regular cXML Invoice file includes the following elements for attachments and the extrinsic image scan:

<Comments>
	<Attachment>
		<URL>https://www.google.com/images/srpr/logo11w.png</URL>
		//If your attachment is publicly accessible by a URL, specify the URL.
	</Attachment>
	<Attachment>
		<URL>cid:test.pdf</URL>
		//If we have the attachment locally, then specify the content-ID in URL.
	</Attachment>
</Comments>
<Extrinsic name="ImageScanAttachmentUrl">cid:test.csv</Extrinsic>
//Specify ImageScans with the content-ID.

Script output

Your script should save the output to a file so you can POST it to your customer's instance using curl.

curl -X POST -H 'Accept: application/xml' -H 'Content-Type: multipart/related; boundary=<boundary_value_that_you_have>; type=text/xml; start=<cXML_Invoice_content_id>' --data-binary @<output_file_name>.xml https://<customer_name>.coupahost.com/cxml/invoices
Warning

Once the output file is generated, DO NOT OPEN it. Without opening it just POST it to the customer instance. Opening the output file then POSTing it will result in 502 Bad Gateway error.

Add attachments to cXML invoices and test with Postman

Example encoded invoice

--ec6xAZMGCwZhdyY0 //Random Boundary
Content-type: text/xml; charset=UTF-8	//Content type
Content-ID: <payload.xml> //content ID for cXML Invoice file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cXML.org/schemas/cXML/1.2.020/InvoiceDetail.dtd">
<cXML version="1.2.020" xml:lang="en-US" timestamp="2018-03-08T13:08:23-08:00" payloadID="2018-03-08T13:08:23-08:00">
	<Header>
		<From>
			<Credential domain="dwqsdg">
				<Identity>vcbv</Identity>
			</Credential>
		</From>
		<To>
			<Credential domain="vfdfvb">
				<Identity>dbfg</Identity>
			</Credential>
		</To>
		<Sender>
			<Credential domain="dwqsdg">
				<Identity>vcbv</Identity>
				<SharedSecret>fdbgfg</SharedSecret>
			</Credential>
			<UserAgent>Coupa Procurement 1.0</UserAgent>
		</Sender>
	</Header>
	<Request deploymentMode="production">
		<InvoiceDetailRequest>
			<InvoiceDetailRequestHeader invoiceDate="2018-03-08T13:08:23-08:00" operation="new" purpose="standard" invoiceOrigin="supplier" invoiceID="COUPATEST21">
				<InvoiceDetailHeaderIndicator />
				<InvoiceDetailLineIndicator isAccountingInLine="yes" />
				<PaymentTerm payInNumberOfDays="30" />
				//Lines 35-43 are for Attachments
				//Use filenames as content-IDs so it's easy to recognizing which content-ID refers to which encoded format
				<Comments>
					<Attachment>
						<URL>https://www.google.com/images/srpr/logo11w.png</URL>
					</Attachment>
					<Attachment>
						<URL>cid:test.pdf</URL>
					</Attachment>
				</Comments>
				<Extrinsic name="ImageScanAttachmentUrl">cid:test.csv</Extrinsic>
			</InvoiceDetailRequestHeader>
			<InvoiceDetailOrder>
				<InvoiceDetailOrderInfo>
					<OrderReference>
						<DocumentReference payloadID="2820" />
					</OrderReference>
				</InvoiceDetailOrderInfo>
				<InvoiceDetailItem quantity="1.0" invoiceLineNumber="1">
					<UnitOfMeasure>EA</UnitOfMeasure>
					<UnitPrice>
						<Money currency="USD">23.34</Money>
					</UnitPrice>
					<InvoiceDetailItemReference lineNumber="1">
						<ItemID>
							<SupplierPartID />
							<SupplierPartAuxiliaryID />
						</ItemID>
						<Description xml:lang="en-US">test items s</Description>
					</InvoiceDetailItemReference>
					<SubtotalAmount>
						<Money currency="USD">23.34</Money>
					</SubtotalAmount>
				</InvoiceDetailItem>
			</InvoiceDetailOrder>
			<InvoiceDetailSummary>
				<SubtotalAmount>
					<Money currency="USD">23.34</Money>
				</SubtotalAmount>
				<Tax>
					<Money currency="USD">0</Money>
					<Description xml:lang="en-US" />
					<TaxDetail purpose="tax" category="USD" percentageRate="0" taxPointDate="2018-03-08T13:08:23-08:00">
						<TaxableAmount>
							<Money currency="USD">23.34</Money>
						</TaxableAmount>
						<TaxAmount>
							<Money currency="USD">0</Money>
						</TaxAmount>
						<TaxLocation xml:lang="en">USD</TaxLocation>
					</TaxDetail>
				</Tax>
				<NetAmount>
					<Money currency="USD">23.34</Money>
				</NetAmount>
			</InvoiceDetailSummary>
		</InvoiceDetailRequest>
	</Request>
</cXML>
--ec6xAZMGCwZhdyY0 //Boundary is displayed as the cXML Invoice ends here
Content-Type: text/plain; charset=utf-8	//This content-type refers the test in file
Content-Disposition: inline; filename="test.pdf" //Specify the filename and content-disposition remains smae
Content-Transfer-Encoding: base64 //input is encoded in base64 foramt
Content-ID: <test.pdf> //which file is encoded below

//Base64 multipart encoding starts here
...
JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAg
UiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHlnGuPJbdxhr/3
r+gYWO+ZxNvb94u/SSsntmzBEjxOsIgNYz0z8trZ0UozUja+6L/7KRZZTZ6+
...

--ec6xAZMGCwZhdyY0 //boundary is displayed as above input of file is completed
Content-Type: text/plain; charset=utf-8 //This content-type refers the text/details in file
Content-Disposition: inline; filename="test.csv" //Specify the filename and content-disposition remains same
Content-Transfer-Encoding: base64 //input is encoded in base64 foramt
Content-ID: <test.csv> //which file's is encoded below

...
CjAwMDAxOTQ0MjkgMDAwMDAgbiAKMDAwMDE5NDk1NiAwMDAwMCBuIAowMDAw
MTk1MjE4IDAwMDAwIG4gCjAwMDAyMjAyMTQgMDAwMDAgbiAKMDAwMDIyMDY4
NyAwMDAwMCBuIAowMDAwMjIwOTU2IDAwMDAwIG4gCjAwMDAyMzc3OTYgMDAw
...

--ec6xAZMGCwZhdyY0--
//Base64 multipart encoding ends here