I recently changed my DocuSign integration to use the JWT OAuth flow. To achieve this I have a few classes.
OAuth Client
<?php namespace AppDocuSign; use DocuSigneSignClientApiClient; use DocuSigneSignClientAuthOAuth; use DocuSigneSignConfiguration; use Exception; use IlluminateSupportFacadesLog; /** * Helper class to generate a DocuSign Client instance using JWT OAuth2. * * @see * */ class OAuthClient { /** * Create a new DocuSign API Client instance using JWT based OAuth2. */ public static function createApiClient() { $config = (new Configuration())->setHost(config('docusign.host')); $oAuth = (new OAuth())->setOAuthBasePath(config('docusign.oauth_base_path')); $apiClient = new ApiClient($config, $oAuth); try { $response = $apiClient->requestJWTUserToken( config('docusign.integrator_key'), config('docusign.user_id'), config('docusign.private_key'), 'signature impersonation', 60 ); if ($response) { $accessToken = $response[0]['access_token']; $config->addDefaultHeader('Authorization', 'Bearer ' . $accessToken); $apiClient = new ApiClient($config); return $apiClient; } } catch (Exception $e) { // If consent is required we just need to give the consent URL. if (strpos($e->getMessage(), 'consent_required') !== false) { $authorizationUrl = config('docusign.oauth_base_path') . '/oauth/auth?' . http_build_query([ 'scope' => 'signature impersonation', 'redirect_uri' => config('docusign.redirect_url'), 'client_id' => config('docusign.integrator_key'), 'response_type' => 'code' ]); Log::critical('Consent not given for DocuSign API', [ 'authorization_url' => $authorizationUrl ]); abort(500, 'Consent has not been given to use the DocuSign API'); } throw $e; } } }
Signature Client Service
<?php namespace AppDocuSign; use DocuSigneSignApiEnvelopesApi; use DocuSigneSignClientApiClient; class SignatureClientService { /** * DocuSign API Client */ public ApiClient $apiClient; /** * Create a new instance of our class. */ public function __construct() { $this->apiClient = OAuthClient::createApiClient(); } /** * Getter for the EnvelopesApi */ public function getEnvelopeApi(): EnvelopesApi { return new EnvelopesApi($this->apiClient); } }
Then, in my constructors where I want to use it I’m doing
/** * Create a new controller instance */ public function __construct() { $this->clientService = new SignatureClientService(); $this->envelopesApi = $this->clientService->getEnvelopeApi(); }
Finally, I use it like so
$envelopeSummary = $this->envelopesApi->createEnvelope(config('docusign.api_account_id'), $envelopeDefinition);
But I get an error that reads
DocuSigneSignClientApiException: Error while requesting server, received a non successful HTTP code [400] with response Body: O:8:”stdClass”:2:{s:9:”errorCode”;s:21:”USER_LACKS_MEMBERSHIP”;s:7:”message”;s:60:”The UserID does not have a valid membership in this Account.”;} in /homepages/45/d641872465/htdocs/sites/ita-portal/vendor/docusign/esign-client/src/Client/ApiClient.php:344
I researched this and this would imply that the user is not within the account, but they are. I also checked that this account owns the envelopes that I’m trying to send.
For reference I took inspiration for envelope sending from here: https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-template-remote/
Advertisement
Answer
What I think is happening is that the request is going to the wrong server or the wrong account.
I’d suggest using a packet analyser like Fiddler or Wireshark to log where your requests are headed (or just log the request within your application)
The auth URLs seem to be correct since you’re not getting a 401 unauthorised error but the envelopes and other queries’ must match the base URL located in your account under the Apps and Keys page. It would be of the form demo.docusign.net for our demo environment or xxx.docusign.net for our production environment