Construct a multipart/mixed request body with a batch boundary string; the Content-Type header is multipart/mixed; boundary=batch_<unique-id>
Wrap related write operations (POST, PATCH, DELETE) that must succeed or fail together inside a changeset part: Content-Type: multipart/mixed; boundary=changeset_<unique-id>
Each individual request inside the changeset is a Content-Type: application/http part with its own HTTP verb, relative URL, and JSON body
POST the entire batch to /api/data/vX.X/$batch; Dataverse processes the changeset atomically — if any operation in the changeset fails, all are rolled back
Parse the multipart response: each part maps to the corresponding request, with its own HTTP status code and body — iterate parts in order to correlate responses to requests
To reference a record created earlier in the same changeset by a Content-ID, use $<contentId> in the URL of a later request within the same changeset (e.g., /$1/parentaccountid@odata.bind)
Known gotchas
Read operations (GET) cannot be inside a changeset — they must be in the outer batch as standalone parts; mixing GET and write operations in the same changeset causes the entire batch to be rejected
The Content-ID header value must be a positive integer unique within the changeset; using a string GUID or repeating a value causes Dataverse to reject the reference with an unclear parsing error
Batch requests have an upper limit on the number of operations per batch (check current Dataverse documentation for the exact limit); exceeding it results in a 400 error on the entire batch rather than partial processing
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp