public function send($method, $path, $options = [], $withAuthorization = true)
    {
        try {
            // Increment the number of attempts so we can eventually give up.
            $this->attempts++;

            // Make the request. Any error code will send us to the 'catch' below.
            $response = $this->raw($method, $path, $options, $withAuthorization);

            // Reset the number of attempts back to zero once we've had a successful
            // response, and then perform any other clean-up.
            $this->attempts = 0;
            $this->cleanUp();

            return json_decode($response->getBody()->getContents(), true);
        } catch (\GuzzleHttp\Exception\ClientException $e) {
            $endpoint = strtoupper($method).' '.$path;
            $response = json_decode($e->getResponse()->getBody()->getContents(), true);

            switch ($e->getCode()) {
                // If the request is bad, throw a generic bad request exception.
                case 400:
                    throw new BadRequestException($endpoint, json_encode($response));

                // If the request is unauthorized, handle it.
                case 401:
                    return $this->handleUnauthorizedException($endpoint, $response, $method, $path, $options);

                // If the request is forbidden, throw a generic forbidden exception.
                case 403:
                    throw new ForbiddenException($endpoint, json_encode($response));

                // If the resource doesn't exist, return null.
                case 404:
                    return null;

                // If it's a validation error, throw a generic validation error.
                case 422:
                    return $this->handleValidationException($endpoint, $response, $method, $path, $options);

                default:
                    throw new InternalException($endpoint, $e->getCode(), $e->getMessage());
            }
        }
    }