title: Using wp_remote_post() to post multipart form data with image to iNaturalist REST API
body: <p>I'm developing a WordPress website that allows users to submit observations that are then pushed to the iNaturalist API. I have successfully authenticated, pushed the observation data, and received the ID of the observation from the API. I am completely hung up on pushing a photo with the <a href="https://www.inaturalist.org/pages/api+reference#post-observation_photos" rel="nofollow noreferrer">POST /observation_photos</a> endpoint. My suspicion is that I'm not formatting the payload correctly, but I can't find any working examples of this type of request in a language I'm familiar with, so there's a lot of blind stabbing in the dark.</p>
  
  <p>I've tried a lot of things before posting here so bear with me as I go through them and the results of each.</p>
  
  <p>First, I tried the following:</p>
  
  <pre><code>$photo_payload = [
      'method' =&gt; 'POST',
      'timeout' =&gt; 10,
      'headers' =&gt; [
          'Authorization' =&gt; "Bearer $auth",
          'Content-Type' =&gt; "multipart/form-data;"
      ],
      'body' =&gt; [
          'observation_photo' =&gt; [
              'observation_id' =&gt; $inat_id
          ],
          'file' =&gt;file_get_contents($photo_sized)
      ]
  ];
  $post_photo = wp_remote_post($inat_base_url . '/observation_photos', $photo_payload);
  </code></pre>
  
  <p>Which results in: <code>500 Internal Server Error</code></p>
  
  <p>Then I tried removing the file from the body array and just send:</p>
  
  <pre><code>$photo_payload = [
      'method' =&gt; 'POST',
      'timeout' =&gt; 10,
      'headers' =&gt; [
          'Authorization' =&gt; "Bearer $auth",
          'Content-Type' =&gt; "multipart/form-data;"
      ],
      'body' =&gt; [
          'observation_photo' =&gt; [
              'observation_id' =&gt; $inat_id
          ],
          'file' =&gt; ''
      ]
  ];
  </code></pre>
  
  <p>We get a success and iNaturalist adds a placeholder "Processing..." image to the observation (as seen here: <a href="https://www.inaturalist.org/observations/8014211" rel="nofollow noreferrer">https://www.inaturalist.org/observations/8014211</a>).</p>
  
  <p>So that tells me that the file data is not being sent properly. I tried base64_encoding the image data but got a 500 error for that too.</p>
  
  <p>Next up was this attempt: </p>
  
  <pre><code>$photo_payload = [
      'method' =&gt; 'POST',
      'timeout' =&gt; 10,
      'headers' =&gt; [
          'Authorization' =&gt; "Bearer $auth",
          'Content-Type' =&gt; "multipart/form-data;"
      ],
      'body' =&gt; [
          'observation_photo' =&gt; [
              'observation_id' =&gt; $inat_id
          ],
          'file' =&gt; '@' . $photo_sized . ';filename=' . basename($photo_sized) . ';type=' . get_post_mime_type($photo_id)
      ]
  ];
  </code></pre>
  
  <p>That gave me another <code>500 Internal Server Error</code>.</p>
  
  <p>Then I started down the multipart/form-data rabbit hole and based on this post (<a href="http://codechutney.com/posting-file-using-wp_remote_post/" rel="nofollow noreferrer">http://codechutney.com/posting-file-using-wp_remote_post/</a>) tried:</p>
  
  <pre><code>$boundary = wp_generate_password( 24 );
  
  $body = '';
  $body .= '--' . $boundary . "\r\n";
  $body .= 'Content-Disposition: form-data; name="file"; filename="' . basename($photo_sized) . "\"\r\n";
  $body .= 'Content-Type: ' . $photo_type . "\r\n\r\n";
  $body .= base64_encode(file_get_contents($photo_sized)) . "\r\n";
  $body .= '--' . $boundary . "\r\n";
  $body .= 'Content-Disposition: form-data; name="observation_photo"' . "\r\n";
  $body .= 'Content-Type: application/json' . "\r\n\r\n";
  $body .= json_encode(['observation_id' =&gt; $inat_id]) . "\r\n";
  $body .= '--' . $boundary . '--' . "\r\n";
  
  $photo_payload = [
      'method' =&gt; 'POST',
      'timeout' =&gt; 10,
      'headers' =&gt; [
          'Authorization' =&gt; "Bearer $auth",
          'Content-Type' =&gt; "multipart/form-data; boundary=$boundary"
      ],
      'body' =&gt; $body
  ];
  </code></pre>
  
  <p>Alas, another <code>500 Internal Server Error</code>.</p>
  
  <p>Seriously, I have been banging my head against the wall on this for so long that I can't remember all the things I tried. At some point, I know I was getting timeout errors too, but I can't recreate them right now.</p>
  
  <p>Other notes: It doesn't seem to change anything if I change the endpoint from observation_photos to observation_photos.json. The file that I am trying to send is 7585 bytes.</p>