Starting point
The ERP system maps the client's core processes: contracts, routes, billing runs and quotes. It runs as a Laravel application with a Vue 3 interface (Inertia), MySQL as the database and Redis for queues and cache, in Docker containers.
Documents such as invoices, quotes and route plans are not created in the ERP but in a workflow system. The ERP triggers the workflows via API and gets the result back. I am the lead developer of this integration, and also of billing, routes, frontend and deployment.
Problem
- 2.1Large files in the webhook. For the bulk printing of route plans, the workflow system delivered the PDF Base64-encoded inside the webhook. The request hung, and the process ran into its memory limit.
- 2.2Short-lived API access. The workflow system's token is valid for only two minutes. During long-running processes, requests were rejected.
- 2.3Manual deployments. New versions were deployed directly on the server with git pull, composer install, yarn build and migrate.
Decisions
Moving the webhook into a queue
The webhook endpoint now only accepts the request and hands the work to a queue job (Horizon). The request answers immediately and no longer hangs, even when processing takes longer.
Signed webhook
The workflow system signs every call with HMAC-SHA256 over timestamp and content. The ERP checks signature and timestamp (30 minutes tolerance) and rejects everything else.
Bulk printing via URL instead of Base64
Instead of sending large files in the webhook, the workflow system now delivers a URL for bulk printing as soon as all documents have been generated. The Base64 path remains in the code only for older deliveries, where the PDF stays in the cache for 15 minutes at most.
Managing the access token centrally
The token lives centrally in the cache and is discarded earlier than it expires. A lock makes sure only one process logs in again. If a request is rejected anyway, the client fetches a fresh token and retries once.
private function authenticateWithLock(): void{ // Only one process logs in, the others wait Cache::lock('workflow_token_lock', 15)->block(10, function (): void { if (! $this->loadTokenFromCache()) { $this->requestNewToken(); } });}
Deployment via image and pipeline
Instead of deploying updates by hand on the server, the pipeline builds a Docker image on every change and triggers the deployment via webhook. The containers have separate roles for web, scheduler and queue worker. Next, the deployment moves to Coolify.
Result
- 4.1The webhook answers immediately; processing runs asynchronously in the queue.
- 4.2Large bulk prints burden neither the request nor memory, because no files arrive in the webhook any more.
- 4.3Rejected requests are retried once with a fresh token instead of aborting a long-running process.
- 4.4Deployments are reproducible: every version is an image, rolled out by the pipeline.
Want a similar system built for you? Find out more under Services or get in touch via Contact.