I’m using Dart/Flutter with the http package. The code that runs after the return from the OAuth validation is:
var uploadUrl =
'$proto://$apiHost/api/$api/gpx/create';
var mp = MultipartFile.fromString('file', sampleGPX, // see solution below!
filename: 'filename1',
contentType: MediaType('application', 'gpx+xml'));
var request = MultipartRequest('POST', Uri.parse(uploadUrl))
..files.add(mp)
..headers['Authorization'] = 'Bearer $bearerAuth'
..fields['file']= 'filename1'
..fields['description'] = 'GPX track upload'
..fields['visibility'] = 'public';
if (!prodMode) {
debugPrint('Request: ${request.toString()}');
debugPrint('Request headers: ${request.headers}');
debugPrint('Request fields: ${request.fields}');
debugPrint(
'Request file: ${request.files[0].filename} ${request.files[0].contentType}');
}
var response = await client!.send(request);
When invoked, the printout is:
Request: POST https://master.apis.dev.openstreetmap.org/api/0.6/gpx/create
Request headers: {Authorization: Bearer ...redacted...}
Request fields: {file: filename1, description: GPX track upload, visibility: public}
Request file: filename1 application/gpx+xml; charset=utf-8
And the response.statusCode is 400.
I’d be most grateful if anyone can see what I’m doing wrong. Thanks.
P.S. Obviously a more detailed message in the status (“Bad request”) would help track down such errors 
Answering my own question, it turns out that the first argument to MultipartFile.fromString() has to be ‘file’. This is NOT documented. I had ‘part1’ in there, but have emended it to ‘file’ so when somebody blindly copies the example they won’t waste the same time as I did on this.