lsnepomuceno / laravel-autentique
A Laravel client for the Autentique GraphQL API v2: electronic signatures, documents, signers, folders and webhooks
Package info
github.com/lsnepomuceno/laravel-autentique
pkg:composer/lsnepomuceno/laravel-autentique
Requires
- php: >=8.4.1 <8.6
- ext-json: *
- illuminate/console: ^13
- illuminate/contracts: ^13
- illuminate/filesystem: ^13
- illuminate/http: ^13
- illuminate/routing: ^13
- illuminate/support: ^13
- nesbot/carbon: ^3
- symfony/http-foundation: ^7.4 || ^8.0
Requires (Dev)
- ext-tokenizer: *
- ergebnis/composer-normalize: ^2.52
- larastan/larastan: ^3.10
- laravel/pint: ^1.30
- nunomaduro/collision: ^8
- orchestra/testbench: ^11
- pestphp/pest: ^5.0
- pestphp/pest-plugin-laravel: ^5.0
- pestphp/pest-plugin-type-coverage: ^5.0
- phpstan/phpstan: ^2.2
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- roave/security-advisories: dev-latest
- shipmonk/composer-dependency-analyser: ^1.8
- webonyx/graphql-php: ^15
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-21 18:39:17 UTC
README
A Laravel client for the Autentique GraphQL API v2: electronic signatures,
documents, signers, folders, organizations, webhooks, the Corporate endpoint and OAuth.
Every operation is a .graphql file validated against the API's schema, every answer a typed value object,
and every request goes through Laravel's HTTP client, so Http::fake() and Autentique::fake() reach it.
Documentation · Getting started · Public API · Upgrading · Changelog
What it does
- Every operation is a
.graphqlfile in the package, sent with variables and validated against the API's schema, never a string built at runtime. - Typed in, typed out. Signers, positions and verifications are built as value objects, and every answer comes back as one, with enums and dates.
- Refused before it is spent. What Autentique documents it would refuse, or silently change, is refused before a request.
- One transport, Laravel's HTTP client, so
Http::fake()reaches every request, andAutentique::fake()makes testing your own code trivial. - Webhooks verified against
X-Autentique-Signatureon the raw body, and delivered as a Laravel event.
It replaces
lsnepomuceno/laravel-autentique-v2,
unfinished since 2021; UPGRADE.md maps every part of it.
Installation
composer require lsnepomuceno/laravel-autentique
AUTENTIQUE_TOKEN=your-api-token AUTENTIQUE_SANDBOX=true # local and staging only
php artisan autentique:check # is the token accepted, and whose account is it
Every key has an environment variable and a default, so publishing
config/autentique.php is optional:
php artisan vendor:publish --tag=autentique-config
Documents
use LSNepomuceno\LaravelAutentique\Data\Input\{Position, Signer}; use LSNepomuceno\LaravelAutentique\Enums\{Action, Reminder}; use LSNepomuceno\LaravelAutentique\Facades\Autentique; $document = Autentique::newDocument('Service agreement') ->file($request->file('contract')) // or a path, or Autentique::fromDisk('s3', 'contracts/1.pdf') ->signer(Signer::email('ana@example.com')->withPosition(Position::signature(x: 50, y: 90))) ->signer(Signer::whatsapp('+5554999999999', Action::Approve)) ->reminder(Reminder::Weekly) ->send(); $document->id; $document->signatures[0]->publicId;
The file can be a path, an upload, Autentique::fromPath(),
Autentique::fromUpload() or Autentique::fromDisk(), and it is streamed in
every case. The same call without the builder:
Autentique::documents()->create($newDocument, $signers, Autentique::fromPath('/tmp/contract.pdf'));
Everything else about a document:
Autentique::documents()->find($id); Autentique::documents()->list(status: DocumentStatus::Pending); // Data\Page<Document> Autentique::documents()->update($id, new DocumentChanges(name: 'Revised')); Autentique::documents()->block($id); // nobody can sign from now on Autentique::documents()->delete($id); // the trash; it does not stop signing Autentique::documents()->moveToFolder($id, $folderId);
Signers of an existing document
Autentique::signers()->add($documentId, Signer::email('late@example.com')); Autentique::signers()->resend([$publicId]); Autentique::signers()->link($publicId)->shortLink; Autentique::signers()->approveBiometric($verificationId, $publicId);
Folders and organizations
$folder = Autentique::folders()->create('Contracts 2026'); Autentique::folders()->share($folder->id, [Share::withEmail('legal@example.com', FolderRole::Editor)]); Autentique::folders()->documents($folder->id); Autentique::organizations()->current()->groups; Autentique::organizations()->emailTemplates(); Autentique::account()->me()->subscription?->documents;
Webhooks
AUTENTIQUE_WEBHOOK_SECRET=the-endpoint-secret AUTENTIQUE_WEBHOOK_PATH=webhooks/autentique
Event::listen(function (AutentiqueWebhookReceived $received) { if ($received->event->type === WebhookEventType::DocumentFinished) { // $received->event->documentId() } });
Every webhook is verified against X-Autentique-Signature on the raw body
before anything runs.
Corporate and OAuth
Experimental: neither has run against Autentique yet, so both may change in a minor release until they have (public API).
// The Corporate plan: child organizations, members, plans, webhook endpoints. $child = Autentique::corporate()->createOrganization(new NewChildOrganization(name: 'Branch office')); // OAuth: act on behalf of accounts that authorize the application. $authorization = Autentique::oauth()->begin([OAuthScope::UserRead, OAuthScope::DocumentsCreate]); $tokens = Autentique::oauth()->callback($request, $state, $verifier); Autentique::withToken($tokens->accessToken)->account()->me();
Errors
Every failure is an LSNepomuceno\LaravelAutentique\Exceptions\AutentiqueException,
narrowed to the fault: ValidationFailed with its codes per field, NotFound,
RateLimited with the wait Autentique asked for, Unauthenticated,
InsufficientScope, TransportFailed, InvalidInput for what was refused
before sending, and a few more. Autentique reports most errors with HTTP 200;
the package reads them for you.
Testing
$autentique = Autentique::fake(); // … the application runs … $autentique->assertDocumentSent(fn(array $document) => $document['name'] === 'Service agreement');
Nothing is sent, no token is needed, and every answer is built from what was sent.
Anything the package does not model
$data = Autentique::query('query ($id: UUID!) { document(id: $id) { hashes { sha2 } } }', ['id' => $id]);
Compatibility
| Requirement | Version |
|---|---|
| PHP | 8.4.1 to 8.5 |
| Laravel | 13 |
| PHP extensions | json, plus what Laravel itself requires; curl recommended |
json and hash (for the webhook signature) ship enabled in every PHP 8
build. The HTTP client, Guzzle under Laravel's, uses curl when it is loaded
and PHP streams otherwise, which need allow_url_fopen on and openssl
loaded, since Autentique is reached over HTTPS.
lsnepomuceno/laravel-autentique-v2, for Laravel 8 and PHP 7.4 to 8.0, is no
longer maintained; UPGRADE.md maps it to this package.
Verified against Autentique
The suite never reaches Autentique, and needs no token. What it is checked against was taken from the live API:
- The schema.
tests/Resources/schema.graphqlis introspected from the standard endpoint, and every operation file is validated against it, as are the examples of Autentique's own Postman and Altair collections. - A sandbox run of documents, signers, folders, organizations and the account, which corrected one signature and five enums the documentation had wrong.
- Real webhook deliveries, whose signatures verified and whose payload is a fixture of the suite.
The Corporate endpoint and OAuth are the exception, and are experimental until they run too.
Contributing
Patches are expected to come with tests. composer check runs everything CI
runs: Pint, composer normalize, PHPStan at level max with no baseline, the
dependency analyser, the suite and 100% type coverage.
docker compose -f .docker/compose.yaml run --rm php85 composer check
See CONTRIBUTING.md, and ARCHITECTURE.md for how the package is put together and why. The rules that break the product when violated are in docs/spec/invariants.md, and the reasoning behind the design is one numbered file per decision in docs/decisions/.
Security
Found a vulnerability? Please follow SECURITY.md rather than opening a public issue.
License
MIT. See LICENSE.md.