GraV-IT
← All work

2019 · Lead developer · Oct 2019 – Apr 2026

SignerX API

For SignerX

Lead developer for an in-flight Laravel SaaS API from 2019 to 2026 — shaped the codebase as it exists today through three major Laravel version migrations, a hybrid-encryption middleware that decrypts requests transparently to downstream code, 25 queued jobs orchestrating video transcoding and connected-service syncing, an event-driven workflow engine across 25+ domain events, and a 71-test feature suite that gates every PR.

Stack
Laravel PHP MySQL Redis (Horizon) Laravel Cashier (Stripe) Laravel Passport (OAuth2) Postmark

The brief

SignerX is a document-signing SaaS — recipients open a private link, fill the fields, sign, and the package routes itself through a multi-step workflow to the next signer or the finalizer. I led the API as the primary developer from October 2019 to April 2026. The codebase existed before me — what’s mine is what it became across those years: three Laravel major-version migrations, a multi-year build-out of the workflow engine, and an encryption layer protecting sensitive payloads end-to-end.

I’m not the original author. What’s mine is the shape of the codebase as it stands now — how it’s organized, how it scales, how it’s tested, and how it stayed current through a six-year run.

What I shaped

A request-level encryption layer. A DecryptPayloadMiddleware does hybrid encryption end-to-end: clients encrypt the payload with AES-256-CBC, encrypt the symmetric key with the server’s RSA public key, and send both. The middleware decrypts the key with the private RSA key, decrypts the payload with AES, and merges the plaintext back into the request — controllers downstream are none the wiser. A companion RequireEncryptionMiddleware enforces the policy on routes that handle anything sensitive; legacy clients fall back to direct RSA; snake-case normalization on the decrypted fields keeps the API surface clean.

A domain-organized data model. Forty-six Eloquent models split across eight domains — Auth, Billing, CMS, ConnectedServices, Documents, OAuth, Support, Workflow — with all six relationship types in active use, custom pivot models where the pivot tables earned them, ordered relationships, and self-referential trees (folders, sub-fields). The kind of discipline that pays off the moment you onboard a new developer or chase a multi-table query.

A queue-driven workflow engine. Twenty-five jobs running on Redis + Supervisor + Horizon. The interesting ones aren’t the cleanups — they’re the video pipeline (Qencode-orchestrated transcoding with an FFmpeg fallback and chunked uploads for large files), the connected-services sync (every completed package routes itself to the customer’s Dropbox or Google Drive automatically), and the recipient-reminder cadence that nudges signers without nagging them.

Event-driven orchestration. Twenty-five-plus domain events drive the package lifecycle. A PackagePublishedEvent fans out to email dispatch and activity logging; a RecipientSignedEvent updates state and attempts package completion; a PackageCompletedEvent triggers the final-PDF generation and connected-services sync. Listeners do substantive work, and the event surface ends up being the most honest documentation of how the platform actually moves.

A test suite that earns the term. Seventy-one feature test files covering the feature surface — packages, recipients, fields, subscriptions, the video pipeline, the encryption layer — each gated in CI on every PR. The test bench is what made three Laravel major-version migrations (7 → 8 → 9.52, one version at a time) survivable without a rewrite.

What’s notable about it

Six and a half years of disciplined ownership. The codebase stayed current with Laravel through every major version, the data model absorbed feature growth without re-architecture, and the workflow engine absorbed every “what if we also did X” the product team asked for without rebuilding the spine. The longevity wasn’t an accident — it’s what disciplined ownership of someone else’s codebase looks like when the goal is to make it last, not to put your own name on it.