What's the correct `content-type` when editing posts?

Hey folks, can I use application/json as Content-Type when updating a post via the API? The docs say I can, but I am starting to think I cannot

I constantly get ["BAD CSRF"] and I have no clue what that means.


If I need to use multipart/form-data do you folks have any pointers on how I build my put request? Particularly the data.

header = CaseInsensitiveDict()
header["Authorization"] = '{"api-key": "longapikey", "api-username": "myusername"}'
{
  "post": {
      "raw": "Cool post, but here's an updated to the post's body",
      "edit_reason": "I changed this because I can."
   }
}

resp = requests.put(url, headers=headers, data=data)

Thanks!

You need to send the Api-Key and Api-Username as their own headers, not in the Authorization header. Something like this should work better:

header = CaseInsensitiveDict()
header["Api-Key"] = 'longapikey'
header["Api-Username"] = 'myusername'

This looks like the same issue as your previous topic:

2 Likes

Thanks for your reply, David.

That’s super weird, when I format the header the way you showed, I get:

{"errors":["You are not permitted to view the requested resource. The API username or key is invalid."],"error_type":"invalid_access"}

If I add the ["Authorization"], it works.

That’s for a simple GET request, I still can’t do the PUT.

I would expect a header that works for one operation to work for all (provided the key is global - which it is). So I am not too worried about the header at the moment - or should I be?

Thanks!

Likely something is malformed with your PUT request then if GET is working when you are doing it the wrong way with the Authorization header. It is working because you don’t have to be authorized at all the make the GET request. We don’t even look at the Authorization header if you pass it in. GET requests to public endpoints will work fine without any headers.

1 Like

@pedroleaoc here is a little sample python script that demonstrates how to make authenticated requests and how to send the request data for put/post requests.

# discourse-api-demo.py
import requests
from requests.structures import CaseInsensitiveDict

# Basic GET request to a public url, no headers needed.
url = "http://localhost:3000/posts/10.json"

resp = requests.get(url)

print(resp.status_code)
print(resp.content)

# GET request to a private endpoint. Authentication headers are needed.
url = "http://localhost:3000/admin/users/list/active.json"
headers = {'Api-Username': 'system', 'Api-Key': '5c1c57915e2...'}
resp = requests.get(url, headers=headers)

print(resp.status_code)
print(resp.content)

# PUT request with a request body
url = "http://localhost:3000/posts/10.json"
data = { 'raw': "Cool post, but here's an updated to the post's body", 'edit_reason': "I changed this because I can." }

resp = requests.put(url, headers=headers, json=data)
print(resp.status_code)
print(resp.content)

From Quickstart — Requests 2.26.0 documentation

Instead of encoding the dict yourself, you can also pass it directly using the json parameter (added in version 2.4.2) and it will be encoded automatically:

url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'} >>> r = requests.post(url, json=payload)

Note, the json parameter is ignored if either data or files is passed.

Using the json parameter in the request will change the Content-Type in the header to application/json .

1 Like

Doh, of course! Not all GETs require Authorization!
That’s such a nice pointer, I will look into that. Thanks!

Things that I already know by reverse engineering:

  • api-key rather than api_key
  • The PUT request requires "content-type": "application/x-www-form-urlencoded"
  • The data is not in JSON (it’s encoded, although I don’t seem to find the right encoding)

Please see my post just above this. You can use the json=data instead of the data=data format in your request and the python requests library will take care of the content-type for you and will set it as application/json which is what you should be using.

2 Likes

Awesome, I am now able to edit my post! Thanks for your help!

There’s only one more issue, the key I am using is global. When I try with a key with write and read permissions only, I get: You are not permitted to view the requested resource. The API username or key is invalid.. When I edit the post via the GUI, there seems to be a PUT request to the topic’s URL (apart from the one that actually edits the post posts/post_id.json) which I can’t reproduce with a limited API key, only the global. I don’t see why, however, I wouldn’t be able to edit the post via the API even without this extra PUT request that happens in the GUI.

EDIT: Technically, my API key covers /t/:slug/:topic_id which is where the PUT request is pointing to.

Do you have the “edit Posts” scope selected for your api key?

No! I don’t even have that option! Will look into that, thanks for the help once again.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.