Skip to Content


This page is for explaining how to test the OData $batch services with Content ID, which is implemented in the H2G.

In this H2G, we’re going through the steps of testing OData $batch services with Content ID by REST Client.

In the test steps, we’ll create a $batch request that contains one parent entity with Content ID $100, and two children.


Two testing tools are explained:

SAP Gateway Client

In the SAP tx SEGW, find your Gateway project and select “Service Maintenance” > “Your GW System” > “SAP Gateway Client”.


In a SAP Gateway Client tool, enter following request information:

Note: SAP Gateway Client handles all the authentication info such as X-CSRF-Token or Basic Auth behind the scenes.

  • HTTP Method = POST
  • Request URI = the URL has $batch suffix
  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • Payload body = enter the batch script


And execute it – confirm the successful response.



Start a POSTMAN REST client. Type the URL of your OData service document URL (the URL that ends with “_SRV/”). Set following params:

  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • X-CSRF-Token = Fetch
  • Authentication – (basic auth credentials)


Execute the request by pressing “Send”. You should be able to obtain the returned x-csrf-token value. Copy it – this is required to execute HTTP POST later.


Paste the token value for X-CSRF-Token param in the header. Set following params:

  • HTTP POST – the URL has $batch suffix
  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • X-CSRF-Token = <token>
  • Authentication – (basic auth credentials)


Now your POSTMAN is fully ready for sending the $batch payload. Click on “Body” and select raw.


Here you’ll enter the payload. (Explanation follows)

01 --batch_mybatch
02 Content-Type: multipart/mixed; boundary=changeset_mychangeset1
04 --changeset_mychangeset1
05 Content-Type: application/http
06 Content-Transfer-Encoding: binary
08 POST HeaderSet HTTP/1.1
09 Content-Type: application/xml
10 Content-Length: 1021
11 Content-ID: 100
13 <?xml version="1.0" encoding="utf-8"?>
14 <entry xml:base="http://..._SRV/"
15     xmlns=""
16     xmlns:m=""
17     xmlns:d="">
18     <content type="application/xml">
19         <m:properties>
20             <d:Id>1</d:Id>
21             <d:Text>New parent entity</d:Text>
22         </m:properties>
23     </content>
24 </entry>
26 --changeset_mychangeset1
27 Content-Type: application/http
28 Content-Transfer-Encoding: binary
30 POST $100/ToItems HTTP/1.1
31 Content-Type: application/xml
32 Content-Length: 1021
34 <?xml version="1.0" encoding="utf-8"?>
35 <entry xml:base="http://..._SRV/"
36     xmlns=""
37     xmlns:m=""
38     xmlns:d="">
39     <content type="application/xml">
40         <m:properties>
41             <d:ParentId></d:ParentId>
42             <d:Id>000010</d:Id>
43             <d:Text>First child entity</d:Text>
44         </m:properties>
45     </content>
46 </entry>
48 --changeset_mychangeset1
49 Content-Type: application/http
50 Content-Transfer-Encoding: binary
52 POST $100/ToItems HTTP/1.1
53 Content-Type: application/xml
54 Content-Length: 1021
56 <?xml version="1.0" encoding="utf-8"?>
57 <entry xml:base="http://..._SRV/"
58     xmlns=""
59     xmlns:m=""
60     xmlns:d="">
61     <content type="application/xml">
62         <m:properties>
63             <d:ParentId></d:ParentId>
64             <d:Id>000020</d:Id>
65             <d:Text>Second child entity</d:Text>
66         </m:properties>
67     </content>
68 </entry>
70 --changeset_mychangeset1--
72 --batch_mybatch--

#01 – The beginning of the $batch request. It has to start with “–” with the batch name, which is the same name you defined in the header of Content-Type = multipart/mixed;boundary=batch_mybatch.

#02 – Content type definition of the changeset.

#03 – This line space is very important. At least one “carriage return line feed” is required.

#04 – The beginning of the changeset. It has to start with “–” with the changeset name, which is the same name you defined in the line #02.

#05/06 – Conventional value for Content-Type and Content-Transfer-Encoding.

#07 – At least one “carriage return line feed” is required.

#08/09 – POST request for HeaderSet entity.

#10 – Content-Length value must be equal to or greater than the real length of the data below.

#11 – A line of the Content ID value.

#12 – At least one “carriage return line feed” is required.

#13 to 24 – The HeaderSet payload. You can obtain this payload format via Read operation. If you obtain the payload via Read, that should contain the tags like <id/>, <title/>, <updated/>, <category/>, and <link/>. You can keep them in this $batch payload or just discard them – the OData services simply ignore them.

#25 – At least one “carriage return line feed” is required.

#26 – As the next entity is a part of the same changeset, it has the same value with the line #04.

#27 to 29 – Same rule follows in the ItemSet.

#30 – POST request for ItemSet entity. As you see, the Content ID is being used.

#31 to 33 – Same rule follows in the ItemSet.

#34 to 46 – The ItemSet payload. Just like you did for HeaderSet entity, you can obtain this payload format via Read operation. Please note this payload has the empty value for <parentID> tag. During OData $batch processing, this value will be handled correctly via the Content ID value.

#47 to 69 – The second HeaderItem POST request.

#70 – The closing of the changeset. It has to start with “–” with the changeset name AND closing “–” characters.

#71 – At least one “carriage return line feed” is required.

#72 – The closing of the batch request. It has to start with “–” with the batch name AND closing “–” characters.

Let’s execute the $batch request by pressing Send button. If the $batch processing is done correctly, you should be able to see “201 Created” responses for each entity in the returned payload body.

Note: As written, the line spacing is critical (it is a part of the standard OData specification).


Time to experiment further… try set the remote breakpoint in the ABAP editor – and see how the changeset_begin, changeset_process, and changeset_end are called sequentially.


To report this post you need to login first.


You must be Logged on to comment or reply to a post.

    1. Kenichi Unnai
      Post author

      Hi Siva,

      All I can tell you right here is to debug the gc_method_get. You’ll be able to tell why you get the malformed syntax error..


  1. Elton Alimeta

    Hello Former Member .

    Firstly start your body code with the code above (replacing the existing code from line 1 to line 5)
    Content-Type: multipart/mixed; boundary=changeset_123


    Content-Type: application/http
    Content-Transfer-Encoding: binary


    At the end the changeset and the batch must be closed



    Hope this would help  😉

      1. Elton Alimeta

        Hi .

        If it were a problem of the BATCH script formatting it would had returned the code 400 bad request,
        Code 500 does not have to do with the formatting of the BATCH ,
        check your header parameters please if are alimented correctly , if yes then put a break point in the method you are calling via the batch (POST  GET PUT) .

        Thnx .

        1. Former Member


          The Changeset_process method code was right. The only change I did now is, I given “” instead of “” in payload:


          Content-Type: multipart/mixed; boundary=changeset_mychangeset



          Content-Type: application/http

          Content-Transfer-Encoding: binary

          It’s now working fine. Thanks for your help. Excellent post by Kenichi Unnai

  2. Elton Alimeta

    Hello Kenichi Unnai .

    Actually I am trying to use the batch to improve the performance of my custom ODATA service that creates SalesOrders using a Bapi .

    The payload we are using is that of the type deep entity (each XML payload contains all the data of the header and item ).

    I was wondering if there is a way to tell that I am using the $batch processing in order to set a different processing logic when we are in $batch.

    Is there any chance i can have inside an internal table all the Payloads of the change sets so i can  populate all the tables of the BAPI in one shot and if it ever fails for any kind of error in any of the item , the posting (update/create) should result in an error and not be executed.

    Actually the processing of the payloads is done sequentially , which means if I am going to create a document  with 4 positions and supose position 3 should block the document creation , we fristly create the document then for the second line we go on modify of that document adding an item and so on , but this is kind of wrong .

    Because if one of the items should go wrong the document must not be created at alla, so thats why I need to have all the items in one Internal Table so i can do the create or update calling the BAPI only once per document .

    Any idea or solution on this would be appreciated.


    1. Kenichi Unnai
      Post author

      Hi Toni,

      Just caught up with your question.


      Are you familiar with the LUW of ABAP? As all the $batch execution is in the same LUW scope, if you execute BAPIs inside, it will be rolled back in case any BAPI execution fails.


      Best regards,


      1. Elton Alimeta

        Hi Ken.
        Yes I am familiar with the Logical Unit of Work 🙂 .

        In fact my main concern was how to handle all the payloads infos  in one internal table , but actually i solved my issue using 3 FMs  of the same Function Group .
        1- Z_APPEND_BATCH “-> append payloads inside global ITAB of the FG
        2- Z_RETRIVE_ITAB_BATCH “->retrive the ITAB with all payloads once we are at the last changeset .
        3- Z_REFRESH_ITAB_BATCH “-> clear the ITAB for the next uses .

        Inside the Loop of the table IT_CHANGESET_PROCESS  used to call my 1-st FM  in order to collect all the data in the GLOBAL Internal Table of the FG .

        In order to find the last request we set a logic with a client so we decided to add a field to the structures of the payload that showed the last row of that particular SO .

        So after looping all the changesets I had all the payloads inside my Itab and run the control on the last record in order to set the logic to populate the the BAPI’s structures and tables right after the check.

        Now I have only one question if you have experienced it before ,
        Since I am collecting all the payloads before calling the BAPI at the last call , the method needs a response in the   IT_CHANGESET_RESPONSE  parameter ,
        but unless I execute the BAPI cant know the fate of the SO if it will be created or will have problems, and still need to send a response back .
        Can you please address any method how to send my own custom responses , of type S, I or W ,
        Already tried some methods using message containers but the custom message wont display unless i throw an exception , which despite the type of message I used to create , turns on display an error message .

        Thanks again.

        1. Former Member

          Hello Former Member,

          I am trying to pass Header and Line Item in the payload.

          I could find the header in the changeset_begin method but I am not able to find the line item. It show only one entity type ie the header.

          This is the payload  I am sending , Request you to help me on this


          Content-Type: multipart/mixed; boundary=changeset_01869434-0005-0001

          Content-Type: application/http
          Content-Transfer-Encoding: binary

          POST SalesOrders HTTP/1.1
          Content-Type: application/atom+xml
          Content-Length: 1021

          <?xml version=”1.0″ encoding=”UTF-8″?>
          <entry xmlns=”” xmlns:m=”” xmlns:d=””>
          <content type=”application/xml”>


          Content-Type: application/http
          Content-Transfer-Encoding: binary

          POST OrderItems HTTP/1.1
          Content-Type: application/atom+xml
          Content-Length: 1021

          <?xml version=”1.0″ encoding=”UTF-8″?>
          <entry xmlns=”” xmlns:m=”” xmlns:d=””>
          <content type=”application/xml”>





Leave a Reply