Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.digifist.com/llms.txt

Use this file to discover all available pages before exploring further.

Galantis is a multi-tenant Laravel application that sits between Shopify and the WhatsApp Business Platform. It ingests data from Shopify via webhooks, processes it through an event-driven engine, and dispatches messages through the Meta Cloud API. Each merchant workspace operates in full isolation — tenant data, credentials, and configuration are never shared across workspaces.

Stack overview

Shopify Store

  ├── Webhooks → Galantis webhook endpoints
  ├── GraphQL API ← ShopifySdkService (polling, billing, webhook management)


Galantis App Layer (Laravel 12)
  ├── Merchant Dashboard        Inertia.js + React
  ├── Automation Engine         Node-graph, event-driven, per-customer execution
  ├── Campaign Engine           Batch message dispatch, throttled by phone throughput
  ├── Catalog Sync              Shopify → Galantis → Meta product pipeline
  ├── Inbox                     Conversation management + agent routing
  ├── Billing Engine            Credits, plans, usage tracking

  ├── Queue Layer               Redis + Laravel Horizon
  │    └── Jobs                 Message sending, data sync, monitoring, billing

  ├── Real-time Layer           Laravel Reverb
  │    └── Broadcasts           ConversationMessageCreated → merchant dashboard

  ├── Database                  PostgreSQL
  │    ├── Structured fields     Customer profiles, orders, messages, templates
  │    └── JSONB columns         Automation nodes/edges, customer tags, message content

  └── AI Layer                  LarAgent framework
       └── Flow Builder AI      Goal interpretation, validation, optimization suggestions


Meta / WhatsApp Cloud API (Graph API v23.0)
  ├── Message API               Send and receive messages
  ├── Template API              Create, submit, sync approval status
  ├── Media API                 Upload images, video, documents (resumable protocol)
  └── Catalog API               Push product data

Multi-tenant isolation

Galantis uses Stancl/Tenancy to provide complete database schema isolation between merchant workspaces. Each installed Shopify store is a separate tenant with its own isolated PostgreSQL schema. This means:
  • Customer data, order history, message records, automation configurations, campaign history, and credentials are never shared across tenants
  • A query in one tenant’s context cannot access another tenant’s data — isolation is enforced at the database level, not just the application level
  • Shopify access tokens, Meta access tokens, and catalog tokens are stored encrypted per-tenant and are never accessible outside their owning workspace
Tenant resolution happens from the request context — typically subdomain or a request header depending on the access path (dashboard vs API). Stancl/Tenancy bootstraps the correct tenant schema before any application logic runs.

Event-driven processing

The core processing model is event-driven:
Shopify webhook received
  → Signature validated
  → Handler job dispatched to Redis queue
  → Job processes payload
  → Domain event fired (e.g., OrderCreated, CustomerTagsAdded)
  → Domain event listeners run
  → Automation trigger evaluation
  → Customer enrollment if trigger matches
  → Automation node execution begins
This pattern decouples webhook receipt from processing — the webhook endpoint returns a 200 immediately, and the actual work happens asynchronously via the queue. This makes the system resilient to processing latency and transient failures. The queue is managed by Laravel Horizon, which provides job monitoring, retry management, and throughput visibility. Redis is the backing store for the queue.

Automation engine

Automations execute per customer, per flow, independently. When a trigger fires for a customer, a separate execution context is created for that customer in that automation. Multiple customers can be in different stages of the same automation simultaneously — each moves through nodes independently. Node execution is sequential within a customer’s context:
  • DelayNode execution schedules the next node execution at a future time via the queue
  • ConditionNode execution evaluates the condition and dispatches the next node based on the YES or NO result
  • ActionNode execution calls the Message API and records the result in AutomationActivity
Every node execution is recorded in AutomationActivity with status PENDING → SCHEDULED → COMPLETED / FAILED / SKIPPED. This provides the full audit trail accessible in the automation activity log.

Campaign engine

Campaigns use a batch dispatch model via SendCampaignMessagesBatchJob. The audience is resolved at send time — consent filtering, deduplication, and exclusion rules are applied — and the qualifying recipients are processed in batches through the Message API. MonitorCampaignJob runs during and after dispatch to tally delivery statuses from the incoming Meta webhooks and update the campaign-level sent/delivered/read/failed counts. When all messages have a terminal status, the campaign status is updated to SENT, PARTIALLY_SENT, or FAILED, and a notification is triggered. Throughput is constrained by WhatsApp’s per-phone-number message limits — the batch dispatch respects these limits to avoid rate limit errors from the Meta API.

Data storage patterns

Structured PostgreSQL tables — Standard relational tables for entities with well-defined schemas: customer profiles, orders, messages, conversations, templates, automations, campaigns, and billing records. PostgreSQL JSONB columns — Used where schema flexibility is needed without migration overhead:
EntityJSONB usage
Automation flowsnodes and edges arrays storing the full flow graph
Customer tagsTag arrays that vary per customer and change frequently
Message contentTemplate variable mappings and message body data
Product dataFull Shopify product JSON payload stored alongside structured fields
JSONB provides efficient querying on structured JSON without requiring a separate document store, and allows Galantis to store the full Shopify payload per product (in data) alongside the extracted, indexed fields.

Real-time broadcasting

The merchant Inbox updates in real time without polling. When an inbound message arrives from Meta:
  1. ProcessShopifyProductUpdatedJob [sic — in this context: the Meta inbound message handler] creates the Message record
  2. The ConversationMessageCreated event is fired
  3. Laravel Reverb broadcasts the event to the merchant’s dashboard WebSocket connection
  4. The Inbox updates instantly — the new message appears without a page refresh
Laravel Reverb is Galantis’s WebSocket server. It handles the persistent connections between the merchant dashboard and the application layer, enabling push-based updates for conversation activity.

Queue layer

All heavy and asynchronous operations run through the Redis queue managed by Laravel Horizon:
Job categoryExamples
Data syncProcessShopifyCustomerCreatedJob, ProcessShopifyProductUpdatedJob
Message sendingSendCampaignMessagesBatchJob, automation Action Node dispatch
Catalog syncSyncMetaCatalogJob
MonitoringMonitorCampaignJob, MonitorBillingSubscriptionsJob
ComplianceProcessShopifyCustomersRedactJob, ProcessShopifyShopRedactJob
Failed jobs are retried with exponential backoff. Horizon provides visibility into queue depth, job throughput, and failed job details for operational monitoring.