400 Bad Request on Content File/Topic
I'm a D2L LMS Admin working on a custom tool for a page/widget, and I've hit a persistent roadblock with the Content API that I'm hoping someone can shed some light on.
The Goal
I'm trying to create a feature within a custom HTML/JavaScript tool that generates a CSV file on the client side and automatically uploads it as a new, hidden topic into a specific content module.
The Problem & Evidence from Postman
Every attempt to upload the file fails with a 400 Bad Request
and the detail: "Request has missing or invalid parameters."
The most important finding is that this error is reproducible in Postman.
My Postman setup is as follows:
- Authentication: I am using my active browser's session
Cookie
andX-CSRF-TOKEN
in the request headers. This method works for all my other API calls, confirming authentication is succeeding. - Method:
POST
- URL:
https://[my-lms-domain]/d2l/api/le/1.50/[orgUnitId]/content/modules/[moduleId]/structure/
- Body (form-data): A request with a
file
part (containing atest_names.csv
) and atopicData
part (containing the required JSON string).
Here is a screenshot of my Postman setup and the response:
My Method
Below is the JavaScript code from my widget. This is the exact logic I replicated in my failing Postman test.
// Assume orgUnitId, moduleId, csvBlob, and csvFileName are already defined
const token = localStorage.getItem("XSRF.Token");
const formData = new FormData();
const topicData = {
"Title": "DRAR Test CSV File",
"ShortTitle": "DRAR Test CSV",
"Type": 1,
"Url": csvFileName, // Matching the filename of the blob
"IsHidden": true,
"IsLocked": false,
};
// Add the file part and the JSON metadata part
formData.append('file', csvBlob, csvFileName);
formData.append('topicData', JSON.stringify(topicData));
const apiUrl = /d2l/api/le/1.50/${orgUnitId}/content/modules/${moduleId}/structure/
;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': token,
'Accept': 'application/json'
},
credentials: 'include', // Automatically handle session cookies
body: formData
});
What I've Already Tried
I've spent a significant amount of time trying to resolve this and have attempted the following:
- Reordering the
formData
parts (file first vs. topicData first). - Removing the
Url
property from thetopicData
JSON. - Sending the
topicData
as aBlob
instead of a string. - Manually adding the
Cookie
header instead of usingcredentials: 'include'
. - Confirming that the
orgUnitId
andmoduleId
are correct and that I have the necessary permissions (I can manually upload to the target module).
My Question for the Community
Given that this standard request fails even in a controlled tool like Postman, can anyone from D2L or the community identify what might be wrong? Is there a hidden permission, an undocumented required header, or a known issue with this endpoint that would cause this behavior?