Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Docusign Rest Client Laravel Package

tucker-eric/docusign-rest-client

A PHP client for the DocuSign REST API, offering fluent access to envelopes, templates, recipients, and OAuth authentication. Simplifies integrating DocuSign eSignature workflows into PHP and Laravel applications.

Deep Wiki
Context7

Getting Started

This is a thin convenience layer over docusign/docusign-php-client. It does not hide DocuSign's REST API from you — it just removes the repetitive auth/bootstrap boilerplate. Budget time to learn the official eSignature REST API docs alongside it; the wrapper's own surface area is small.

Install

composer require tucker-eric/docusign-rest-client

This pulls in the official DocuSign PHP SDK and Guzzle. Confirm compatible Guzzle/SDK versions in composer.lock before you build on it — version skew with the official client is the most common install failure.

Gather four things from DocuSign first

  1. Integration Key (OAuth client ID)
  2. Account ID (and the user GUID for JWT)
  3. API username / password (only for legacy X-DocuSign-Authentication; prefer OAuth)
  4. An RSA private key (JWT Grant) or client secret (Authorization Code Grant)

Decide demo vs. production up front — they are entirely separate accounts with separate keys.

Where to look first

Read src/ top to bottom before anything else. It is small. You want to identify:

  • the client constructor and the exact config array keys it expects (names vary between wrapper versions — do not trust blog posts)
  • where the OAuth login / token retrieval happens and whether the token is cached or fetched per call
  • how resource groups are exposed (envelopes, templates, folders, users) and how calls are proxied to the underlying SDK
  • which exceptions it lets bubble up (usually the SDK's ApiException)

First use case

Send an envelope from a server-side template and read its status back. That single flow exercises config, auth, envelope definition, recipient roles, and error handling — everything that will bite you later.

use DocuSign\eSign\Model\EnvelopeDefinition;
use DocuSign\eSign\Model\TemplateRole;

$definition = new EnvelopeDefinition([
    'status'         => 'sent',            // 'created' = draft, does not send
    'template_id'    => config('docusign.template_id'),
    'template_roles' => [
        new TemplateRole([
            'role_name' => 'Signer 1',
            'name'      => $user->name,
            'email'     => $user->email,
            'tabs'      => ['text_tabs' => [/* ... */]],
        ]),
    ],
]);

$summary = $client->envelopes->createEnvelope($accountId, $definition);
$envelopeId = $summary->getEnvelopeId();

Verify the exact accessor names against the source — the wrapper may expose envelopes as a property or a method, and the argument order may be normalized.

Implementation Patterns

Keep credentials out of code. Put them in config/services.php (or .env) and instantiate the client once. The private key should be read from a file path in .env, not inlined.

// config/services.php
'docusign' => [
    'integration_key' => env('DOCUSIGN_INTEGRATION_KEY'),
    'user_id'         => env('DOCUSIGN_USER_ID'),
    'account_id'      => env('DOCUSIGN_ACCOUNT_ID'),
    'private_key'     => file_get_contents(env('DOCUSIGN_PRIVATE_KEY_PATH')),
    'host'            => env('DOCUSIGN_HOST', 'https://demo.docusign.net/restapi'),
],

Register it as a singleton. Instantiation triggers network auth in most builds of this wrapper. In Laravel, bind it once in a service provider and inject it; in plain PHP, hold one instance for the process lifetime.

$this->app->singleton(DocuSignClient::class, fn () => new DocuSignClient(config('services.docusign')));

Choose JWT Grant for anything server-to-server. It is the only flow that does not need a human in the loop. Authorization Code Grant is for multi-tenant apps acting on behalf of arbitrary users — meaning you must persist per-user refresh tokens. Pick deliberately; retrofitting is painful.

Prefer DocuSign Connect webhooks over polling. This is the single biggest architectural decision. Register a Connect configuration in DocuSign pointing at a signed webhook route, verify the HMAC signature, then fetch the envelope only when you actually need its contents. Polling getEnvelope on a schedule will hit rate limits at any real volume and burn through your API quota.

Route::post('/webhooks/docusign', function (Request $r) {
    abort_unless($r->header('X-DocuSign-Signature-1') && $this->verifyHmac($r), 403);

    $envelopeId = $r->input('data.envelopeId');
    $status     = $r->input('data.envelopeSummary.status');

    match ($status) {
        'completed' => CompletedEnvelopeJob::dispatch($envelopeId),
        'declined', 'voided' => FlagEnvelopeJob::dispatch($envelopeId, $status),
        default => null,
    };

    return response()->noContent();
})->withoutMiddleware(['csrf']);

Separate "create draft" from "send". Create with status => 'created', persist the envelope ID, then call an update to flip to sent. This lets you attach tabs, documents, and custom fields across multiple operations without the envelope racing out the door.

Wrap every call. The SDK throws ApiException with the DocuSign error body in getResponseBody(). Decode it, log the errorCode / message, and translate into your domain exceptions. Never let a raw 400 escape to a user.

try {
    $summary = $client->envelopes->createEnvelope($accountId, $definition);
} catch (\DocuSign\eSign\ApiException $e) {
    $body = json_decode($e->getResponseBody(), true);
    Log::error('docusign.envelope.create', ['code' => $body['errorCode'] ?? null, 'message' => $body['message'] ?? null]);
    throw new EnvelopeCreationFailed($body['message'] ?? 'Unknown DocuSign error', previous: $e);
}

Download documents as a combined PDF via the documents API (documentId => 'combined') rather than iterating documents individually — one request instead of N, and it preserves page order.

Make it testable. The wrapper is a concrete class, so bind an interface in your container and put a fake behind it in tests. Do not call the real API from your test suite; hit the demo environment manually and keep CI hermetic.

Gotchas and Tips

JWT consent is a one-time browser ritual. Before your first JWT call works, a human must visit the consent URL once per integration key + user combination:

{basePath}/oauth/auth?response_type=code&scope=signature%20impersonation&client_id={INTEGRATION_KEY}&redirect_uri={REDIRECT_URI}

Skip this and every JWT call returns consent_required. It is not obvious and it accounts for a large share of first-day failures.

Tokens expire in 8 hours. If the wrapper fetches a token inside the client and you re-instantiate per request, you are hitting the auth endpoint on every page load. Cache the access token (Redis/keyed by user) with a safety margin, and confirm the wrapper supports injecting an existing token. If it does not, you may need to subclass or bypass it for token management.

basePath is not a constant. The login response returns the correct baseUrl for that account — https://demo.docusign.net/restapi in the sandbox, but something like https://na4.docusign.net/restapi in production, varying by account region. Hardcoding na4 works until it does not. Let the auth response drive it.

status => 'sent' sends immediately. Omit it (or use created) and you have a draft nobody receives. This is the #1 "why didn't my envelope go out" bug.

Template role names must match exactly — including case and trailing spaces — and must match the role names configured on the template. A mismatch silently produces an envelope that needs manual reassignment, with no error at creation time.

Dates are ISO 8601 and the API is UTC. If you set expiration or reminders, convert from your app timezone explicitly. Off-by-a-day expiry bugs are tedious to trace.

Envelopes are eventually consistent. Immediately after createEnvelope, a getEnvelope may not yet reflect recipients or custom fields. Do not assert on them in the same request cycle. Use the webhook as the source of truth.

Rate limits are per-account and real. DocuSign publishes limits and will throttle. Batch operations, cache envelope metadata you need repeatedly, and treat 429 as a signal to back off — not to retry immediately.

Go-Live is a separate project. The demo environment is free; production requires a DocuSign review of your integration. Start that process early, especially if you handle PHI or regulated data (the package gives you no compliance features — that is entirely on your app).

Debug by logging at the Guzzle layer. When the wrapper swallows or obscures a failure, push a Guzzle handler/middleware that logs method, URL, status, and raw body. DocuSign's error messages are usually accurate and specific — the problem is almost always that they are not surfacing.

The extension point is the underlying SDK. The wrapper's value is auth and ergonomics; models and resource classes come straight from docusign/docusign-php-client. When you need something the wrapper does not expose, reach for the underlying client rather than fighting it — and consider whether the 31-star wrapper is worth keeping as your only abstraction layer for a business-critical flow.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin