Skip to Content
Technical Articles
Author's profile photo Matti Leydecker

What is form-data and how to send it from SAP Cloud Platform Integration (CPI)


Our team solved an interesting challenge involving SAP Cloud Platform Integration and an API expecting to receive form-data today.

We faced an API which accepted a POST request using the Postman tool but would always throw an error when ‘exactly the same content’ (we will learn more about that later) was sent from the CPI.

Because the POST request needed to send form-data, this strange behaviour made us dig into the formal definition of form-data by W3C. We also found some very nice blogs like this one by Pieterjan who explains form-data from a little bit different angle.

In this blog, we will explain the concept of form-data, how to send it from Postman and how to send it from the Cloud Platform to another system.

What is form-data anyway?

Form-data originates from HTML forms which take user input and send it through the browser to a web server. The same technology can be used to send data between applications other than browsers.

It comes in two flavours: application/x-www-form-urlencoded and multipart/form-data. Both are used to send key-value mappings. In HTML forms, a field is represented as a mapping of the name of the field to the content. 

Urlencoded form-data is the standard way but not sufficient for sending large quantities of binary data also known as files. For more information see the W3C definition.

Two ways of sending form-data

The rest of this blog will talk about the content type multipart/form-data. Don’t be scared by the multipart, it also works if your request contains only one part.

First, we will explore how to send the form-data through Postman. This is easily done with the below settings. The body is set to form-data and key-value pairs can be added. Note that it is possible to add simple strings, formatted data like JSON and also files as values.

Postman takes care of the rest behind the scenes. To recreate this request in the SAP Cloud Platform Integration we first recreated this request in Postman using the raw content type. This is needed as the CPI does not support form-data directly but has only the basic capabilities to alter header and body.

The information we needed to enter was gathered from the W3C definition in combination with Postman’s handy feature to generate code from a request by clicking on Code (in the top right corner of the screenshot above). The details will follow in the next section.

Header and body

Two things are important in the generated code to create the form-data: the header Content-Type and the body formatting.

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

The header labels the content as form-data and introduces the boundary, which is a user-chosen string. Postman generates this one for us. Don’t worry about the multipart, this is used for all form data, even if it contains only one part like in our examples.

Content-Disposition: form-data; name="formElement"


The body declares form-data again but also gives the data a name, this is the key in the Postman example in the previous section. We also see our “ExampleValue” which is the value in the said example. Please note that the boundary in the body has two more dashes “–” in the beginning than the boundary declared in the header. This is the rule per the definition.

More key-value pairs could be added in the body here but let’s keep it simple for now. The very last boundary needs to also have to dashes in the end (compared again to the boundary in the header).


To the Cloud Platform Integration

In the CPI you would now go ahead and create the header:

With the value (note the much simpler but still working boundary):

Content-Type: multipart/form-data; boundary=cpi

and the body:


Content-Disposition: form-data; name="formElement"


Normally our story would end here. The CPI sends out the well-formatted form-data and we are happy.

The anomaly

In our case, the API we were trying to talk to would accept the form-data from Postman with either way, raw or as form-data. If we copied the exact same header and body which worked in Postman to the CPI we’d get an HTTP 400 error “Unable to parse multipart body”. This happens for example when you leave out the newline between Content-Disposition and the ExampleValue in the example above.

After observing different payloads over and over again we tried not to create the form-data in the CPI but send it there from Postman and route it through to the API. Normally for our use-case, we need to send a SOAP message but as nothing was working this seemed like something we could give a try.

And it was successful. With the message from Postman routed through the CPI, the API accepted the request. So we checked what the difference was between the request we created “by hand” in the CPI (see chapter To Cloud Platform Integration) and the one coming from Postman.

Even comparing them side by side in the CPI tracing view showed no difference. We also created an Iflow that stripped away all headers but the Content-Type but with no luck.

Finally, we compared the two payloads in Notepad++ which can show hidden characters like line ending characters (View -> Show Symbol -> Show All Characters). It seems Postman on Windows creates the “correct” line ending characters and the CPI creates UNIX line endings which our API could not handle. Turns out it wasn’t ‘exactly the same data’.

The solution

To solve our problem we created a script which would automatically convert the linefeeds. You can find it below should you ever run into this kind of trouble.

import java.util.HashMap;
def Message processData(Message message) {
       def body = message.getBody(String);
       body = body.replaceAll("\n", "\r\n");
       body = """--cpi\r\nContent-Disposition: form-data; name="envelope"\r\n\r\n\r\n""" + body + """\r\n\r\n--cpi--"""

       return message;


In this blog we explained what form-data is, showed the different forms and how to send it through Postman. Then we moved to the Cloud Platform Integration and sent form-data from there. In the end we showcased an error with an API that threw an error on UNIX line endings and how to fix it.

Thank you for reading! Please let me know if you faced similar problems or if you have further questions regarding form-data requests or other CPI topics.

Assigned tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Eng Swee Yeoh
      Eng Swee Yeoh

      Hi Matti


      Thanks for sharing this. Few months back, there was an interesting question that came up in the forums that had to post form-data to Leonardo's MFLS OCR service.


      Forming the form-data part with Content Modifier did not work in that case because the content was binary, and the Content Modifier would corrupt the content because it would undergo conversion to string.


      Here's my solution for that case using Groovy Script for that case, which could cater for both binary or text content. Might be of interest to you 🙂


      Best regards,

      Eng Swee


      Author's profile photo Matti Leydecker
      Matti Leydecker
      Blog Post Author

      Hi Eng Swee,

      while searching for an answer for the anomaly described in this blog, I came across your answer on that particular problem!

      Naive that I was at that point I thought it can't be that complicated - having to compare files on a binary level. Turns out I was wrong and we had to dig quite a bit into the file/encoding level here.

      I will keep this in mind going forward and use some parts of your script for convenient multipart sending 🙂

      Thank you for your comment.




      Author's profile photo Eng Swee Yeoh
      Eng Swee Yeoh

      Hi Matti


      Nevertheless, your blog highlights some of the challenges and what to look for in form-data interfaces, and that is really useful 🙂

      The whole idea of comparing the payload with Postman is a useful troubleshooting approach. Another approach that I would recommend (which I used myself in the above case) was to route the message through a proxy that captures the details of the HTTP call. You can find out more in the following blog - that has been a life saver many times round.



      Eng Swee


      Author's profile photo Nick Yang
      Nick Yang

      Hi Matti,

      I encountered very much exactly same issue early this year like you experienced in this blog.

      I was trying to post zip file inside multipart form-data to Ariba Network server from CPI.

      The content produced by my groovy script looks exactly same as same one from PostMan but server always returned "invalid content length". I suspected HTTP adapter might work in a different way than I thought.

      In the end, my work around solution is to post form and binary content through groovy script like below and it works.

      def Message postZipContentToAriba(Message message){
          def messageLog = messageLogFactory.getMessageLog(message)
          def propertiesMap = message.getProperties()
          def headersMap = message.getHeaders()
          // POST
          def post = new URL(propertiesMap.get("lAriba-Network-URL")).openConnection();
          def zipContent = message.getBody()
          post.setRequestProperty("Content-Type", headersMap.get("Content-Type") as String)
          def postRC = post.getResponseCode();
          if(postRC.equals(200)) {
              messageLog.addAttachmentAsString("Post result:", post.getInputStream().getText() as String, "text/plain")
              def exceptionMsg = "HTTP" + postRC.toString() + ", " + post.getInputStream().getText()
              throw new Exception(exceptionMsg as String)
          return message


      Kind regards,


      Author's profile photo Matti Leydecker
      Matti Leydecker
      Blog Post Author

      Hi Nick,

      thank you for your comment!

      That's a strange error, glad you could fix it - the only thing is that doing HTTP calls from groovy is really discouraged...


      Best regards


      Author's profile photo Joao Miguel Gomes
      Joao Miguel Gomes

      Hello Nick,


      This is really interesting, because I'm running into the exact same problem; the Content-Length not defined error.

      I've tried nearly everything, but with no success.

      My current script is very similar to the one Eng Swee posted in the other blog, but I'm really curious in understand your approach.


      At the moment I'm testing your script and I'm running into the following error:

      java.lang.NoSuchMethodException: No signature of method: is applicable for argument types: ( values: [] Possible solutions: write([B), write(int), write(int), write(int), wait(), wait(long)


      I'm using SFTP Adapter to pickup the file to send to Ariba. The only thing in the iflow is the Script Process.

      Can you kindly give your opinion on this matter?

      Thank you in advance.


      Author's profile photo Nick Yang
      Nick Yang

      Hi João,


      Based on error message, the type of input parameter in below code is unacceptable.


      The type of variable zipContent in my script is byte array and it's the output of combination of form + binary content (zip file). If you want to discuss further, then create a question post and I'll answer in the new post.


      Kind regards,



      Author's profile photo Joao Miguel Gomes
      Joao Miguel Gomes

      Hello Nick,

      I've created another question. It's on this URL:

      Thank you in advance for providing your insights on this topic.

      Kind regards,

      João Gomes

      Author's profile photo Prashanth Bharadwaj
      Prashanth Bharadwaj

      Hello Matti,

      Thank you so much for the blog.. I have similar kind of requirement. However, I need to pass as below with the following Body in form-data. Kindly help on the same.

      grant_type: client_credentials
      client_secret: XXXXXXXXXXXXXXXXXXXXX
      Author's profile photo Matti Leydecker
      Matti Leydecker
      Blog Post Author

      Hi Prashanth,

      you can probably just use the OAuth Adapter, Type: Client Credentials to do this type of POST request.

      If it needs to be form-data, you can refer to my guide above and add your key value pairs accordingly. In Postman you can show the final request by clicking on "Code" next to the Send Button or "</>" in the newest version of Postman.

      Best regards


      Author's profile photo Prashanth Bharadwaj
      Prashanth Bharadwaj

      Hello Matti,

      Thank you so much for the reply. I have tried as below(attached Screenshot). Please correct me if the following parameters are incorrect. Currently, I am facing 400 bad request error. Kindly help.




      Content Modifier- Body



      Prashanth Bharadwaj.Ch

      Author's profile photo CARLOS BURITICA

      Hello Matti,

      Thank you so much for the blog. I have similar kind of requirement, but it does not work:



      I am facing 400 bad request error. Kindly your help please.