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

Echosign Bundle Laravel Package

ets/echosign-bundle

ETSEchoSignBundle is a Symfony wrapper for Adobe EchoSign APIs, offering services to send, inspect, and remove documents. Install via Composer, configure API credentials and recipients, then use the bundled client services.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: this is not a Laravel package. ets/echosign-bundle is a Symfony 2 bundle that wraps the Adobe EchoSign SOAP API (EchoSignDocumentService16). It ships an ETSEchoSignBundle class meant for AppKernel::registerBundles() and config.yml — there is no service provider, no facade, and no config/*.php publishable config. Your first task is deciding whether to (a) run it inside a Symfony app that already exists, or (b) harvest its service/model classes into a small Laravel adapter that you own.

Where to look first, in order:

  1. src/ETS/EchoSignBundle/Resources/config/services.xml — this is the real API surface. It tells you the service IDs (typically ets_echo_sign.api or similar) and which constructor arguments (api key, gateway, wsdl) get injected.
  2. The main client class the XML points at — usually a thin SoapClient wrapper with methods like sendDocument, getSigningUrl, getDocumentInfo, getDocumentsForUser, sendReminder, cancelDocument.
  3. DocumentCreationInfo / FileInfo / RecipientInfo model classes in the same namespace — these map one-to-one onto the SOAP request payload.
  4. Tests/ — for a package this small, the tests are usually the only real documentation of the expected call shape.
  5. composer.json — check the Symfony/PHP constraints before you commit. Assume dev-master only, no tags.

Minimal bring-up in Laravel (since the bundle won't install cleanly into a Laravel app):

composer require ets/echosign-bundle:dev-master
// config/services.php
'echosign' => [
    'key'     => env('ECHOSIGN_API_KEY'),
    'gateway' => env('ECHOSIGN_GATEWAY'), // shard-specific, see Gotchas
    'wsdl'    => env('ECHOSIGN_WSDL'),
],
// app/Providers/EchoSignServiceProvider.php
$this->app->singleton(EchoSignClient::class, fn ($app) => (new EchoSignClient())->configure(
    config('services.echosign.key'),
    config('services.echosign.gateway'),
    config('services.echosign.wsdl'),
));

First use case to build: send a PDF for signature, persist the returned documentKey, then request a getSigningUrl for a specific recipient and redirect the user there. Once that round-trip works against a sandbox account, everything else (widgets, status sync, reminders) is variation on the same three calls.

Implementation Patterns

Wrap, don't sprinkle. The single highest-leverage pattern is a DocumentService / SignatureProvider interface in your app that owns the EchoSign client, so controllers and jobs never touch the bundle's classes directly:

interface SignatureProvider
{
    public function send(Envelope $envelope): string;      // returns documentKey
    public function signingUrl(string $key, string $email): string;
    public function status(string $key): DocumentStatus;
}

This is what makes the package swappable later (Adobe Sign REST API, DocuSign, or a fake in tests).

Sending flow. Build a DocumentCreationInfo with name, message, recipients (signers vs approvers vs CCs are separate arrays and the order of signers defines signing order), and a FileInfo — either DOCUMENT (base64 PDF inline) or URL (a signed S3/public URL EchoSign fetches itself). Strongly prefer URL for anything non-trivial; base64 bloats the SOAP envelope and hits memory/timeout limits fast. Then:

$key = $client->sendDocument($creationInfo);   // -> documentKey
Document::create(['key' => $key, 'status' => 'OUT_FOR_SIGNATURE', ...]);

Embedded signing. Two variants exist: createWidget + getWidgetSigningUrl (reusable, good for a form embedded in your UI) or getSigningUrl on an existing document (per-recipient, one-shot). Render the returned URL in an iframe. Because these URLs are short-lived/single-use, request them at render time inside the controller — never cache them in your DB or a response cache.

Callback reconciliation (the pattern that actually keeps your DB honest). EchoSign POSTs documentKey + status to a publicly reachable callback URL. Do not trust the payload: on receipt, immediately enqueue a job that calls getDocumentInfo($key) and updates your record from the authoritative response.

Route::post('/webhooks/echosign', function (Request $request) {
    SyncDocumentJob::dispatch($request->input('documentKey'));
    return response('', 200); // must be 200, must be fast
})->withoutMiddleware([VerifyCsrfToken::class]);

Belt-and-braces polling. EchoSign retries callbacks only a handful of times and gives up. A scheduled command that pulls getDocumentsForUser over a recent date window (or re-checks every non-terminal document hourly) is standard practice, not paranoia.

Queue the writes. Every outbound call is network I/O against an external SOAP endpoint. Dispatch sends and reminders as queued jobs with $tries = 3 and backoff; make the send idempotent by keying on your own envelope UUID rather than the documentKey (which you don't have until the call succeeds).

Credential hygiene. EchoSign uses a bare API key, no OAuth. Keep separate keys for sandbox and production, keep them in env() only, and log request metadata (documentKey, status, fault code) — never the full SOAP envelope in production, since the key travels inside it.

Gotchas and Tips

  • Zero momentum. 3 stars, 0 dependents, dev-master only, no releases. Pin a specific commit (dev-master#abc1234) in composer.json; better still, fork the handful of classes you need into app/EchoSign/ so a broken upstream can't break you. Budget for owning this code.
  • Shard mismatch is the #1 silent failure. secure.echosign.com, secure.na1.echosign.com, secure.eu1.adobesign.com are not interchangeable — gateway and WSDL must both point at the shard your API key was issued on. Mismatches produce terse faults that read like auth errors.
  • SOAP, not REST. Requires ext-soap (often not enabled by default on shared/PaaS hosts). The SoapClient will try to fetch and cache the WSDL on first use — set cache_wsdl => WSDL_CACHE_DISK and, for CI or flaky networks, vendor a local WSDL copy. Also set connection_timeout explicitly; the default can hang a queue worker indefinitely.
  • Faults are opaque. Errors arrive as SoapFault (or the generated EchoSignDocumentService16Exception), and the messages are frequently a code plus nothing useful. Develop with trace => true and dump __getLastRequest() / __getLastResponse() locally. Persist the fault code and a truncated message for production debugging.
  • Callback pitfalls. The route must be CSRF-exempt, unauthenticated, fast, and idempotent — EchoSign retries on non-200 and on slow responses, so the same event can arrive twice. Return 200 from the controller after dispatching the sync job, not after doing the work inline.
  • Statuses are a state machine, not a boolean. OUT_FOR_SIGNATURE, SIGNED, APPROVED, DELIVERED, FORM_FILLED, RECALLED, CANCELLED, EXPIRED. SIGNED is not "done" if you have approvers or countersigners — model the terminal states explicitly and only stop polling on CANCELLED/EXPIRED/RECALLED/final SIGNED.
  • Expiring URLs. getSigningUrl / getWidgetSigningUrl results are time-boxed and sometimes single-use. A user who refreshes the iframe gets a blank page. Re-fetch per render.
  • Reminders are rate-limited, and sendReminder errors if the document isn't currently out for signature. Don't wire it to a button that can be mashed.
  • No tags, no changelog. Check the license and last-commit date before adopting; treat vendor/ets/echosign-bundle as read-only reference material and copy what you need.
  • Extension points for a fork: the SoapClient construction inside the main client class is your single seam — inject a mockable client there rather than trying to stub the bundle's services through Symfony DI from a Laravel container. One integration test against the sandbox, gated behind an env flag, plus unit tests against a faked SignatureProvider, is the right test pyramid for this.
  • PHP 8 / Laravel 10+ compatibility is unverified. Old Symfony 2-era bundles frequently trip deprecations or typed-property assumptions. Test the classes you copy under your actual PHP version before building on them.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib