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.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.
Stack overview
Multi-tenant isolation
Galantis usesStancl/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
Stancl/Tenancy bootstraps the correct tenant schema before any application logic runs.
Event-driven processing
The core processing model is event-driven: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:DelayNodeexecution schedules the next node execution at a future time via the queueConditionNodeexecution evaluates the condition and dispatches the next node based on the YES or NO resultActionNodeexecution calls the Message API and records the result inAutomationActivity
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 viaSendCampaignMessagesBatchJob. 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:| Entity | JSONB usage |
|---|---|
| Automation flows | nodes and edges arrays storing the full flow graph |
| Customer tags | Tag arrays that vary per customer and change frequently |
| Message content | Template variable mappings and message body data |
| Product data | Full Shopify product JSON payload stored alongside structured fields |
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:ProcessShopifyProductUpdatedJob[sic — in this context: the Meta inbound message handler] creates theMessagerecord- The
ConversationMessageCreatedevent is fired - Laravel Reverb broadcasts the event to the merchant’s dashboard WebSocket connection
- The Inbox updates instantly — the new message appears without a page refresh
Queue layer
All heavy and asynchronous operations run through the Redis queue managed by Laravel Horizon:| Job category | Examples |
|---|---|
| Data sync | ProcessShopifyCustomerCreatedJob, ProcessShopifyProductUpdatedJob |
| Message sending | SendCampaignMessagesBatchJob, automation Action Node dispatch |
| Catalog sync | SyncMetaCatalogJob |
| Monitoring | MonitorCampaignJob, MonitorBillingSubscriptionsJob |
| Compliance | ProcessShopifyCustomersRedactJob, ProcessShopifyShopRedactJob |
Related guides
- Authentication — Token storage and OAuth flows
- Webhooks Reference — Full payload reference for all webhooks entering the system
- Domain Events — The internal event layer that connects webhooks to automations