Laravel 12 & 13 · PHP 8.2+ · Acrobat Sign REST API v6
E-signatures, the Laravel way.
conceptlz/adobe-sign-laravel plugs the Acrobat Sign PHP SDK into Laravel. You get
.env configuration, a facade and an injectable client, OAuth routes so each user can
connect their own Acrobat Sign account, an encrypted token table, a webhook endpoint that turns
Adobe's callbacks into Laravel events, and php artisan adobe-sign:verify to check it
all works.
composer require conceptlz/adobe-sign-laravel
What the bridge adds
The core package, conceptlz/adobe-sign, does the Acrobat Sign work: uploading PDFs,
building agreements with a signing order, choosing a signature level, discovering your account's
data centre, retrying throttled calls and parsing webhooks. It is framework-free on purpose, so it
knows nothing about your config files, your cache, your logged-in users or your queues.
This package is the thin layer that connects those two worlds. It contains no Acrobat Sign API logic of its own; every request still goes through the core client. What it adds is the wiring a Laravel developer would otherwise write by hand:
- Configuration. One
config/adobe-sign.phpfile read from.env, covering auth mode, OAuth app, signature level, cache store, retries, log channel and webhooks. - A client you can inject. Type-hint
Conceptlz\AdobeSign\AdobeSignin a controller, job or command, or use theAdobeSignfacade. Both come pre-configured with Laravel's cache, logger and HTTP client. - OAuth without the plumbing. Two routes run the authorization-code flow, check the single-use state, store tokens encrypted with
APP_KEYand fire anOAuthConnectedevent.AdobeSign::forUser($user)then acts with that user's account and refreshes tokens as needed. - A ready-made webhook endpoint. It answers Adobe's verification handshake within the 5-second limit, rejects unknown client ids, drops retried duplicates and fires a
WebhookReceivedevent for your queued listeners. - A credentials check.
php artisan adobe-sign:verifycalls Acrobat Sign and prints who the key or token belongs to.
A first taste
Here is a contract going out from a controller through the facade. The client already knows your Integration Key, your cache store and your project's default signature level, so the code only describes the business process.
use Conceptlz\AdobeSign\Builder\{AgreementInfo, FileInfo, ParticipantSetInfo, WebhookInfo};
use Conceptlz\AdobeSign\Laravel\Facades\AdobeSign;
use Conceptlz\AdobeSign\Webhook\EventType;
public function store(Request $request): JsonResponse
{
$pdf = $request->file('contract');
$documentId = AdobeSign::transientDocuments()->upload($pdf->getRealPath(), 'msa.pdf', 'application/pdf');
$agreementId = AdobeSign::agreements()->create(
AgreementInfo::create('Master Services Agreement')
->file(FileInfo::transientDocument($documentId))
->participantSet(ParticipantSetInfo::signer('client@example.com')->order(1))
->participantSet(ParticipantSetInfo::signer('ceo@example.com')->order(2)),
);
AdobeSign::webhooks()->create(
WebhookInfo::forAgreement("Agreement $agreementId", route('adobe-sign.webhook'), $agreementId)
->events(EventType::AGREEMENT_ALL),
);
return response()->json(['agreement_id' => $agreementId], 201);
}
When everyone has signed, Adobe calls the package's webhook route. The package acknowledges Adobe straight away and fires an event; your queued listener does the slow work:
use Conceptlz\AdobeSign\Laravel\Events\WebhookReceived;
use Conceptlz\AdobeSign\Webhook\AgreementEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
final class StoreSignedContract implements ShouldQueue
{
public function handle(WebhookReceived $received): void
{
if ($received->event instanceof AgreementEvent && $received->event->isWorkflowCompleted()) {
DownloadSignedAgreement::dispatch($received->event->agreementId());
}
}
}
The download job re-checks the status with the API and streams the signed PDF into any Laravel disk. The walkthrough builds the whole loop step by step.
Features
- .env configurationIntegration Key or OAuth, signature level, qualified issuers, cache store, log channel and webhook client ids, all with sensible defaults. See Configuration.
- Facade & injectionThe
AdobeSignfacade forwards toAdobeSignManager; type-hinting the coreAdobeSignclass gives you the default client. - OAuth connect routes
adobe-sign.oauth.redirectandadobe-sign.oauth.callback, behindwebandauth, with single-use state and anOAuthConnectedevent. - Encrypted token table
adobe_sign_tokenskeeps each user's access and refresh token encrypted withAPP_KEY. Tokens refresh automatically and are written back. - Webhook endpoint
GET|POST /adobe-sign/webhook: client-id allow-list, the echo Adobe requires, 72-hour duplicate suppression and a typedWebhookReceivedevent. - Signature levelsSet SES, AES or QES for the whole project in
.env, then check the result withAdobeSign::signatureShortfalls($id). - Production-friendlyThe manager is bound scoped, so it is rebuilt per Octane request and per queued job. The data-centre lookup is cached in your Laravel cache store.
- TestableBind any PSR-18 client in the container and every request goes to it instead of Guzzle. Post a fake webhook to the route and assert on the event. See Testing.
How it fits together
Your application talks to the facade or the injected client. The bridge builds that client from config and Laravel services, and owns the two HTTP entry points Adobe and your users hit. Everything that speaks to Acrobat Sign lives in the core.
The core package and its documentation
Installing this package pulls in conceptlz/adobe-sign. Both packages share one version
number (currently 0.4.0) and are always released together, and the bridge requires the core at
^0.4. All builders (AgreementInfo, ParticipantSetInfo,
FileInfo, WebhookInfo, Verification), resources
(agreements(), transientDocuments(), webhooks(), …), models
and exceptions come from the core and are documented in the
core SDK documentation. This site covers what
is specific to Laravel and shows the core calls in a Laravel setting.
| You want to… | Read |
|---|---|
| Install, configure and verify credentials in Laravel | Installation, Configuration |
| Use the facade, inject the client, act as another user | Using the client |
| Let each user connect their own Acrobat Sign account | OAuth |
| Receive signing progress in queued listeners | Webhooks |
| Learn every builder option, identity checks, downloads | Core: Creating agreements, Identity verification, Downloads |
| Understand SES, AES and QES in legal terms | Core: Signature levels |
Requirements
- PHP 8.2 or newer.
- Laravel 12 or 13 (
illuminate/*^12.0 || ^13.0). The test suite runs on both. - An Acrobat Sign account. A free developer account is enough to build and test; its agreements are marked as demo documents.
- A database table only if you use OAuth (for the tokens), and a public HTTPS URL only if you receive webhooks.
Independent project
This package and the SDK it wraps are developed independently and are not affiliated with or endorsed by Adobe. Adobe and Acrobat Sign are trademarks of Adobe Inc.