Batch URL
The $batch endpoint is available on all API endpoints. For the Business Central SaaS environment you can use this URL:
https://{{baseurl}}/api/v2.0/$batch
Everything I describe here also works for custom APIs. The URL will then look like:
https://{{baseurl}}/api/[publisher]/[group]/[version]/$batch
Request headers
Because the request body will be JSON the Content-Type header of the request must be set to appication/json. If the response should be JSON as well (and you want that!), then the Accept header must also be set to application/json. If you leave the Accept header out, then the response will be multipart/mixed. Here is the basic structure of the request, without the body. I leave out the Authorization header, but you need to add that obviously.
POST {{baseurl}}/api/v2.0/$batch Content-Type: application/json Accept: application/json
The requests array must contain one ore more operations, and each of them must contains an id, the method and a URL and optionally also headers and a body. Here is an example of an operation to insert a single journal line.
{
"method": "POST",
"id": "r1",
"url": "companies({{companyId}})/journals({{journalId}})/journalLines",
"headers": {
"Content-Type": "application/json"
},
"body": {
"accountId": "{{accountId}}",
"postingDate": "2020-10-20",
"documentNumber": "SALARY2020-10",
"amount": -3250,
"description": "Salary to Bob"
}
}
Let’s take the operation above, to insert a single journal line and compose a batch request that inserts multiple journal lines in one go. The request body looks like:
{
"requests": [
{
"method": "POST",
"id": "r1",
"url":
"companies({{companyId}})/journals({{journalId}})/journalLines",
"headers": {
"Content-Type":
"application/json"
},
"body": {
"accountId":
"{{accountId}}",
"postingDate":
"2020-10-20",
"documentNumber":
"SALARY2020-12",
"amount": -3250,
"description": "Salary
to Bob"
}
},
{
"method": "POST",
"id": "r2",
"url":
"companies({{companyId}})/journals({{journalId}})/journalLines",
"headers": {
"Content-Type":
"application/json"
},
"body": {
"accountId":
"{{accountId}}",
"postingDate":
"2020-10-20",
"documentNumber":
"SALARY2020-12",
"amount": -3500,
"description": "Salary
to John"
}
},
{
"method": "POST",
"id": "r3",
"url":
"companies({{companyId}})/journals({{journalId}})/journalLines",
"headers": {
"Content-Type":
"application/json"
},
"body": {
"accountId":
"{{accountId2}}",
"postingDate":
"2020-10-20",
"documentNumber":
"SALARY2020-12",
"amount": 6750,
"description": "Salaries
December 2020"
}
}
]
}
As you can see, each operation has a corresponding result in the response, identified by the id. You should always use the id of the individual operation to find the corresponding result. Don’t assume that the results will always be in the same order as the request! They may seem to be in the same order, but the OData standard describes that the results can be in any order.
Each operation result has a status, which is the HTTP status that you would normally get for a single request. It includes the response headers and response body of the individual operation. The response of the batch request itself will always have status 200 if the server was able to read the batch request. Even if the batch request has operations that couldn’t be processed because of an error condition, the batch response status will still be 200. So don’t only look at the response status of the batch request, you need to read the response body to find out the results of each operation. Only when the batch request body is malformed, or you are not authorized, then the whole batch request will fail and you will get a status 500 (malformed batch request) or status 401 (not authorized).
So far it’s really simple, isn’t it? In the next blog posts in this series, I will cover topics like:
- What happens if an error occurs in one of the requests
- Process a batch as one big transaction
- Combine multiple operations types, like POST and GET
- Define the order of operations
- Reduce the size of the response payload