public function validate($token)
    {
        if ($this->hasCache()) {
            $body = $this->getCache()->get($token);

            // Check if token is expired
            if (!is_null($body)) {
                $body = json_decode($body, true);
                if (new \DateTime($body['expire_at']) >= new \DateTime()) {
                    return $this->buildValidationReturn($body);
                } else {
                    $this->getCache()->delete($token);
                    throw new TokenException('The provided token is expired');
                }
            }
        }

        $request = (new RequestDescriptor())
            ->setUrl($this->buildUrl(sprintf('/api/token/validate?token=%s', (string) $token)))
            ->setMethod('GET');

        try {
            $body = json_decode($this->send($request)->getBody(), true);

            if ($this->hasCache()) {
                $this->getCache()->set(
                    $token,
                    json_encode($body),
                    (new \DateTime())->diff(new \DateTime($body['expire_at']))
                );
            }

            return $this->buildValidationReturn($body);
        } catch (ApiClientException $e) {
            $previous = $e->getPrevious();

            if ($previous instanceof BadResponseException) {
                $error = json_decode($previous->getResponse()->getBody(true), true);

                throw new TokenException($error['error'], $error['code'], $previous);
            }

            throw new TokenException($e->getMessage(), $e->getCode(), $e->getPrevious());
        }
    }