docs: def request(self, method, url, params=None, body=None, **kwargs):
          """
          Send an HTTP request.
  
          The :param:`params` will be properly encoded, as well as :param:`body` which
          will be wrapped with envelope the API expects and json encoded.
  
          When you get a reponse the method will try to json decode the response,
          if the media type represents json, unwrap the envelope and munchify what has left,
          for JavaScript like access.
  
          :param str url: Sub URL for the request. You MUST not specify neither base url nor api version prefix.
          :param dict params: (optional) Dictionary of query parameters.
          :param dict body: (optional) Dictionary of body attributes that will be wrapped with envelope and json encoded.
          :param dict **kwargs: (optional) Other parameters which are directly passed to :func:`requests.request`.
          :raises RequestError: if authentication failed, invalid query parameter etc.
          :raises RateLimitError: if rate limit exceeded.
          :raises ResourceError: if requests payload included invalid attributes or were missing.
          :raises ServerError: if Base CRM backend servers encounterered an unexpected condition.
          :return: Tuple of three elements: (http status code, headers, response - either parsed json or plain text)
          :rtype: tuple
  
          :Keyword Arguments:
              * :param dict headers: (optional) Dictionary of headers. Default: ``{}``.
              * :param bool raw: (optional) Whether to wrap and uwrap the envelope. Default: ``False``.
          """
  
          url = "{base_url}{version}{resource}".format(base_url=self.config.base_url,
                                                       version=self.API_VERSION,
                                                       resource=url)
          headers = {
              'Accept': 'application/json',
              'Authorization': "Bearer {0}".format(self.config.access_token),
              'User-Agent': self.config.user_agent,
          }
  
          user_headers = {}
          if  'headers' in kwargs and isinstance(kwargs['headers'], dict):
              user_headers = kwargs['headers']
  
          headers.update(user_headers)
  
          raw = bool(kwargs['raw']) if 'raw' in kwargs else False
  
          if body is not None:
              headers['Content-Type'] = 'application/json'
              payload = body if raw else self.wrap_envelope(body)
              body = json.dumps(self.wrap_envelope(body), cls=DecimalEncoder)
  
          resp = requests.request(method, url,
                                  params=params,
                                  data=body,
                                  headers=headers,
                                  timeout=float(self.config.timeout),
                                  verify=self.config.verify_ssl)
  
          if not (200 <= resp.status_code < 300):
              self.handle_error_response(resp)
  
          if 'Content-Type' in resp.headers and 'json' in resp.headers['Content-Type']:
              resp_body = munchify(resp.json()) if raw else self.unwrap_envelope(resp.json())
          else:
              resp_body = resp.content
  
          return (resp.status_code, resp.headers, resp_body)