1

I'm trying to post an image to an API, where it image is a URI supplied by another service:

import requests

input_data = {
    "employeeId": 123,
    "picture_uri": 'https://mw1.google.com/crisisresponse/icons/un-ocha/cluster_CCCM_100px_icon_bluebox.png'
}

# retrieve image from URI
r = requests.get(input_data['picture_uri'])

# create dictionary
file = {'photo': r.content}

uri = f"https://api.bamboohr.com/api/gateway.php/subdomain/v1/employees/{input_data['employeeId']}/photo"

response = requests.post(uri, auth=('account','password'), files=file)

print(response.status_code) # 400

What am I missing?

** edit **

Documentation indicates that the POST needs to be a multipart/form-data: https://documentation.bamboohr.com/reference/upload-employee-photo-1

craig
  • 25,664
  • 27
  • 119
  • 205
  • Are there other details included in the response besides 400? Like a verbose description of why it was considered `400 Bad Request`? – Cory Kramer Apr 15 '22 at 13:20
  • @CoryKramer, the response object's `reason` is 'Bad Request'. I don't see any other incidications. – craig Apr 15 '22 at 13:29

2 Answers2

2

The files parameter can't directly be the bytes of the data. It has to be a file-pointer-like object. So you'd need to either save the file or pipe it through io.BytesIO and use the tuple version of files to give it a name:

files = {'file': ('profile.png', io.BytesIO(r.content), 'image/png')}
reponse = requests.post(uri, auth=('account','password'), files=files)

Or better yet, use the data parameter:

headers = {'content-type': 'application/x-www-form-urlencoded'}  # can skip this?
reponse = requests.post(uri, auth=('account','password'),
                        data=r.content, headers=headers)

Edit: If it's supposed to be multipart/form-data then the field names need to match with what they expect. More in the requests toolbelt docs.

aneroid
  • 12,983
  • 3
  • 36
  • 66
  • The first option worked, but the second one did not (with or without that header or `multipart/form-data`). – craig Apr 15 '22 at 14:07
  • In the first version, what would happen if the file is a JPG? – craig Apr 15 '22 at 14:08
  • I think no difference if it's a .JPG or .JPEG. Just a different mimetype `image/jpeg` (note jpeg, not jpg for the mimetype) – aneroid Apr 15 '22 at 15:50
  • For the 2nd version, if it's supposed to be `multipart/form-data` then the field names need to match with what they expect. More in the [requests toolbelt docs](https://toolbelt.readthedocs.io/en/latest/user.html#multipart-form-data-encoder). – aneroid Apr 15 '22 at 15:54
0

Per documentation your request should go to the endpoint f"https://api.bamboohr.com/api/gateway.php/subdomain/v1/employees/{input_data['employeeId']}/.

It also seems to overwrite the existing data, so I'd recommend getting https://api.bamboohr.com/api/gateway.php/{companyDomain}/v1/employees/{id}/

and then posting {**old_data, 'photo': r.content}

Lukas Schmid
  • 1,895
  • 1
  • 6
  • 18
  • The photos endpoint: https://documentation.bamboohr.com/reference/upload-employee-photo-1 – craig Apr 15 '22 at 13:57