product#Laravel#PHP#MySQL#Vue

When I joined Revicit, the product already had the shape of a CRM and campaign tool, but several important concerns were still tied together. Contacts, audiences, campaign delivery, automation, and wallet movement could not remain as isolated features if the platform was going to support more channels and reliable billing.

I worked on the backend side. The work was to separate those concerns, give each one a reliable state, and let them meet at deliberate boundaries. Revicit gives each company one place to manage contacts, audiences, campaigns, connected channels, automation flows, wallet activity, forms, templates, and analytics.

The backend problems I had to untangle

AreaImplemented capabilities
Companies and contactsCompany workspaces, contacts, custom fields, tags, contact history, and ownership checks
CampaignsDraft, scheduled, executed, failed, and retryable campaigns with templates and variables
ChannelsPluggable email, SMS, Facebook, and Instagram adapters behind a shared channel manager
AudiencesStatic and dynamic segments, rule previews, evaluation, and segment contact management
AutomationsEvent, schedule, and manual triggers with nodes, edges, conditions, waits, and actions
WalletBalance, deposits, withdrawals, transaction history, and campaign charges
Other product surfacesContact forms, QR codes, templates, social posts, issue tickets, and analytics

The important part is how these pieces connect. A contact can arrive through a form, enter a segment, become part of a campaign, pass through an automation, and eventually produce a delivery or wallet event. Each step needs to leave enough state for the next step to make a safe decision.

Campaigns separate audience, delivery, and cost

The campaign flow now has clear boundaries:

  1. A company selects a channel, template, contacts, or segments.
  2. The application expands the audience and resolves the channel’s pricing plan.
  3. It estimates the cost and checks that the company wallet can cover the run.
  4. The channel manager sends the message through the adapter for that channel.
  5. Successful and failed deliveries update campaign contacts and execution statistics.
  6. The wallet charges only for successful sends, while failed paths can be retried or reconciled.

The pluggable channel layer keeps email, SMS, Facebook, and Instagram-specific delivery logic out of the campaign controller. The campaign still has one product-level concept: execute this message for this audience through this connected channel.

Automations are stored as flows

An automation has a flow, triggers, nodes, edges, runs, and tasks. The current implementation supports three ways to start a flow:

  • Event trigger: respond to an application event, optionally scoped to a segment.
  • Schedule trigger: start on a configured schedule.
  • Manual trigger: run from the automation operation itself.
Flow building blockWhat it can do
Start and end nodesDefine where a run begins and finishes
Action nodesSend email or SMS, add or remove tags, and update a contact
Condition nodesChoose the next path from contact or run data
Split nodesBranch a flow into multiple paths
Wait nodesSchedule the next task for later
EdgesConnect nodes and carry optional conditions

Each run keeps its own context and status. Each task records its node, status, attempts, scheduled time, output, and last error. That gives the backend a concrete execution trail when a flow pauses or fails.

Segments make the audience usable

Contacts can be organized into two kinds of segments:

  • Static segments contain contacts added or removed manually.
  • Dynamic segments store rules that are evaluated against contact data.

The API has a preview path for testing a rule, an evaluation path for refreshing dynamic membership, and endpoints for adding, removing, and listing segment contacts. That keeps audience selection separate from campaign delivery. A campaign can use a segment without owning the logic that defines who belongs in it.

The money trail is part of execution

The wallet is not only a number shown on a dashboard. A company can fund it, withdraw from it, and inspect the transaction history behind its balance. Campaign execution checks the estimated cost before sending and withdraws based on successful sends. Payment callbacks and delivery reports update the related transaction and campaign state.

That connection is the important part of the project. The system can explain which campaign used the balance, how many messages were successful, and what needs attention when delivery fails. A campaign charge is tied to the work that caused it instead of being a silent subtraction from a shared number.

What the redesign made possible

The backend no longer needs every campaign, audience, channel, and automation path to know about every other one. A new channel can plug into the shared delivery boundary. A segment can change its membership rules without changing campaign execution. An automation can pause, wait, branch, or fail while keeping its own run and task history.

That was the point of the work. Revicit became easier to extend because the product concepts were separated without losing the path between them.