Post content via JSON API: CURL works, PHP not? I've got those modules enabled:

HAL
HTTP Basic Authentication
JSON:API
REST UI
RESTful Web Services
Serialization

When I run this command, the content is created.
curl \
--user demo:9aqW72MUbFQR4EYh \
--header 'Accept: application/vnd.api+json' \
--header 'Content-type: application/vnd.api+json' \
--request POST http://www.demo.drupal/jsonapi/node/article \
--data-binary @payload.json

Now I try to read via PHP, which works fine too.
require 'vendor/autoload.php';

use GuzzleHttp\Client;

// Update to a user on your Drupal site that has permissions to create content
$user = 'demo';
$pass = '9aqW72MUbFQR4EYh';
$url = 'http://www.demo.drupal';

$content_client = new GuzzleHttp\Client([
    'base_uri' =&gt; $url,
    'timeout'  =&gt; 20.0,
]);

$res = $content_client-&gt;request('GET', '/jsonapi/node/mail/71adf560-044c-49e0-9461-af593bad0746');

$body = $res-&gt;getBody();
$stringBody = (string) $body;
var_dump(json_decode($stringBody, true));

I can't post content. The code I am using returns a 401, 402, or 500 error.
$headers = [
  'Content-type' =&gt; 'application/vnd.api+json',
  'Accept' =&gt; 'application/vnd.api+json'
];
$payload['data'] = [
  'type' =&gt; 'node--article',
  'attributes' =&gt; [
    &quot;title&quot; =&gt; &quot;My custom title&quot;
  ]
]; 

$guzzleResponse = $content_client-&gt;post('/jsonapi/node/article', [
  'json' =&gt; json_encode($payload),
  'headers' =&gt; $headers
]);

if ($guzzleResponse-&gt;getStatusCode() == 200) {
  $response = json_decode($guzzleResponse-&gt;getBody());
}

What am I doing wrong?
The 401 error is because you're not sending the basic auth creds like you are with your curl request. You need to add an Authorization header to your Guzzle request:
'Authorization' =&gt; 'Basic ' . base64_encode($user . ':' . $pass),
The 500 error is likely due to a malformed payload that JSONAPI can't parse. The value of json should be an associative array, not a JSON encoded string. See the Guzzle docs on uploading JSON data.