I spent three hours last night, pulling my hair out, while trying to add a custom HTTP header to a StoredTransferJob generated by KIO.storedHttpPost.
In my naivety, I thought KIO.Job.addMetaData(key,value) would accept an HTTP header name as key, and the actual header value as value. I was twice wrong: "key" is a limited list of accepted keywords (documented in the obscure DESIGN.metadata file, buried in the KDE source tree and not linked by any tutorial), and "value" is the complete header (e.g. "Content-Type: application/x-www-form-urlencoded"). It's essential to get the key right, or the metadata will be silently dropped (argh); I had to fiddle with Wireshark to find out. By the way, the two essential keys you probably want to remember are "content-type" and the life-saving "CustomHTTPHeader""customHTTPHeader" (DAMN LOWERCASE C!!! it's wrong in the design document, btw.).
(That's the terrible thing about KDE APIs: lots of barely-documented quirkiness hidden under an appearance of ease-of-use. You start developing thinking that it's all going to be easy, and find out along the way that things are not really as simple as they seem. I guess I should be thankful that at least they have good web tools to browse and grep their source, and fairly complete doxygen docs... but it still hurts more than it should.)