RedFamosa Documentation

RedFamosa — SMM Panel Script

A complete Social Media Marketing panel you install on your own hosting: sell followers, likes and views through a branded storefront, buy them wholesale from upstream providers, and open child panels for your own resellers — each on their own domain.

RedFamosa is a self-hosted PHP script. You upload one ZIP to your server, run a seven-step web wizard and end up with a working panel — no console, no Composer, no build step. Your customers top up a prepaid balance and place orders; the panel routes those orders to your upstream providers and keeps your margin.

What version 1.9.0 includes
  • Seven-step web installer, Production or Demo data
  • Upstream provider manager (standard SMM API)
  • Catalogue: platforms → categories → services
  • Order pipeline with drip-feed, refill and cancel
  • In-app + email notifications for tickets, orders, deposits and sign-ups
  • Broadcast announcements to all customers (bell + optional email)
  • Immutable, auditable wallet ledger
  • Payment methods: manual transfer, Stripe, PayPal
  • Per-method gateway fee: absorbed, added on top, or taken from the balance
  • Top-up bonus tiers and multi-currency display
  • Exchange rates that refresh themselves, with a margin
  • Add-on framework for gateways, themes and extensions
  • Extra landing and client-portal designs installable as theme add-ons
  • An installed design can dress your whole public site — home page, blog, FAQ, terms, catalogue and sign-in — and add sections of its own
  • Sections a design adds are worded by you, in the same Landing Studio
  • Light/dark switch on every public page, remembered per visitor
  • Demo mode: a public demo refuses the actions a restore cannot undo
  • Reseller child panels on their own domain
  • Reseller subscription plans billed from the wallet
  • Plan-gated own provider for resellers
  • Resellers take payments with their own gateways
  • Resellers approve their customers' bank transfers
  • Reseller-only mode: sell panels, not services
  • Self-serve reseller signup from your landing page
  • Public API in the standard SMM format
  • Omnichannel CRM with seven channels
  • AI agents with a knowledge base, plus automations
  • Embeddable web-chat widget
  • Tickets, blog, FAQ, news and landing page
  • Blacklists for IP, link and e-mail
  • Owner and reseller dashboards with charts
  • Three client skins and eight landing skins
  • English, Spanish and Arabic, light and dark mode
  • Full right-to-left (RTL) interface for RTL languages

1.1 Who it is for

RedFamosa is built for the entrepreneur who wants to run an SMM reselling business rather than write one. The typical owner:

1.2 The three roles

RoleAreaWhat they can do
Owner + staff /admin Full administration: providers, catalogue, pricing, payments, child panels, plans, users, tickets, CRM, settings and reports. Staff get a role with granular permissions, so you can hire an operator without handing over the keys to the business.
Reseller /panel Runs a child panel on their own domain. Buys from you at your price, sets their own resale price, manages their own clients, keeps their own CRM inbox, and tops up their balance by paying you.
Client /app The end buyer. Tops up a balance, places orders, follows their progress, opens support tickets and automates through the public API.
One database, strict scoping Every panel — yours and every reseller's — lives in a single database, scoped by panel. The owner never sees a reseller's client data and a reseller never sees another reseller's. Reaching for another panel's record returns a plain 404 rather than an error that would confirm the record exists.

1.3 How to read this manual

Chapters 2 to 4 get you installed and configured — read them in order the first time. Chapters 5 to 8 cover day-to-day operation. Chapters 9 and 10 are the reseller business. Chapters 11 and 12 are the integrations: the public API and the CRM. Chapter 13 is the background worker every panel needs, and chapter 14 collects the things that go wrong, with the fix for each.

Throughout the manual, {APP_URL} means the address of your panel as you entered it during installation, for example https://panel.example.com. Screens are written as Menu → Sub-menu, matching the sidebar of the admin area. All example domains, e-mail addresses and keys are fictitious.

The short version Point your domain at the public/ folder, open it in a browser and complete the wizard (§3). Then add a provider and sync its catalogue (§5), enable a payment method (§8) and choose how background work runs (§13). Everything else is optional.

2. Server requirements

RedFamosa ships with its PHP dependencies and compiled front-end assets already bundled, so the server needs neither Composer nor Node.js. A standard PHP + MySQL hosting account is enough.

PHP

PHP 8.2 or newer (8.3 and 8.4 are supported). The installer compares your version against 8.2.0 and refuses to continue below it.

Required PHP extensions

These thirteen extensions are exactly what the installer checks, one row per line on screen:

ExtensionUsed for
bcmathExact arithmetic in the wallet ledger and price calculations.
ctypeCharacter checks used by the framework and by input validation.
curlOutbound calls to providers, payment gateways and webhooks.
fileinfoReal MIME-type detection for uploaded files.
gdImage processing for avatars and payment proofs.
intlNumber, date and currency formatting.
jsonEncoding of API payloads and stored settings.
mbstringMulti-byte strings and translations.
opensslEncryption of provider keys, gateway secrets and channel tokens.
pdo_mysqlMySQL / MariaDB driver.
tokenizerRequired by the framework itself.
xmlParsing XML responses from some gateways.
zipArchive handling used by exports and updates.

On cPanel these live in MultiPHP Extensions (or Select PHP Version → Extensions). Nearly every host enables all thirteen by default.

Database

Write permissions

The user that runs PHP must be able to write to these five paths. They are the same five the wizard verifies in step 2:

PathWhy
storage/appUploads and the installation lock file.
storage/frameworkCompiled views, sessions and cache.
storage/logsThe application log.
bootstrap/cacheCompiled configuration and routes.
.envThe installer writes your settings into this file. If the file does not exist yet, the project root itself must be writable so it can be created.

On cPanel the PHP user is the account owner, so uploading through File Manager already gives the right ownership. If a check fails, set directories to 755 and the .env file to 644.

Web server

Apache 2.4+ with mod_rewrite and AllowOverride All (the cPanel default), or Nginx with the usual try_files rule for a front controller. The recommended layout points the document root at the public/ sub-folder — but if your host does not let you change the document root (a cPanel main domain locked to public_html, for example), you do not have to: the product ships a root .htaccess that forwards every request to public/ automatically. Both layouts are covered step by step in §3.1.

On Nginx, the try_files rule alone is not enough Several server control panels (aaPanel among them) ship an Nginx template with a rule that catches every address ending in .js and serves it from disk. One of the panel's addresses is answered by PHP rather than being a file, so that rule turns it into a 404 and the JavaScript engine never loads: every screen paints perfectly and no button does anything. One extra block fixes it — the block, and how to confirm it worked, are in §14.1. Apache and LiteSpeed need nothing: the .htaccess files in the download already handle it.
If your host runs ModSecurity, one screen may refuse to save Most control panels enable a web application firewall by default. It can refuse a perfectly ordinary value — a canonical URL or an image path containing ../ is the usual case — and the panel then shows you your server's 403 Forbidden page inside a box. It is not a fault in the panel, and it is a one-line change for your host: §14.1 shows how to identify the rule and what to ask for.
No Composer, no npm The ZIP you downloaded already contains vendor/ (every PHP package) and public/build/ (compiled CSS and JavaScript). You never run composer install or npm install on the server.
Get your certificate first Sessions, payment callbacks and messaging webhooks all behave better over TLS, and some require it. Issue the certificate before you install — on cPanel, AutoSSL does it for free. If the site is still on plain http://, type the address with http:// in the wizard: §14.1 explains what breaks when the two disagree.

3. Installation

RedFamosa installs through a browser wizard of seven steps. Each step validates before it lets you continue, and tells you exactly what to fix without leaving the screen. A normal installation takes about five minutes, most of it spent creating the database in your hosting panel.

What the wizard does for you It writes the .env configuration file, creates every database table, seeds the data you choose, creates your owner account and finally writes storage/app/installed.lock — the file that marks the panel as installed and permanently closes the wizard.

3.1 Prepare your hosting

Do this before opening the wizard. The instructions are written for cPanel because that is what most buyers have; on a VPS the same five points apply with your own tooling.

  1. Upload the product ZIP to your account through File Manager or FTP, and extract it. You get a folder containing app/, public/, vendor/ and the rest of the script.
  2. Point the web server at the script. Pick the path that matches your hosting:
    • Path A — you can change the document root (subdomains, addon domains, and most VPS panels): in cPanel → Domains (or Subdomains), set the document root of the domain to the public folder inside the extracted directory. This is the recommended layout because the application code stays outside the web root.
    • Path B — the document root is locked to public_html (typical for a cPanel main domain): extract the whole product directly into public_html, so that public_html/app/, public_html/public/ and the rest sit at the top level. Nothing else to do — the root .htaccess included in the package forwards every request to public/ automatically and refuses to serve configuration files such as .env directly. Do not move or merge any folders by hand.
    In both paths, requests end up in public/ — landing anywhere else is the reason most "it does not work" tickets exist.
  3. In cPanel → MySQL Databases, create an empty database and a database user, then add the user to the database with All privileges. Write down the database name, user and password.
  4. Issue an SSL certificate for the domain (cPanel → SSL/TLS Status → Run AutoSSL) if you have not already.
  5. Open your domain in a browser. Because the panel is not installed yet, every address redirects to /install and the wizard opens by itself.
There is nothing to do about .env On the very first request the panel notices there is no .env file, copies .env.example over it and generates a fresh APP_KEY for you. That is why the project root must be writable. If you prefer to prepare it yourself, copying .env.example to .env by hand before the first visit works just as well.

3.2 Step 1 — Welcome

The opening screen states what the wizard is about to do and lists the three things you were asked to prepare. Click Get started to continue.

Installer step 1: the welcome screen with the before-you-begin checklist and the Get started button.
What to look at: the progress rail on the left. It stays visible for the whole installation and marks each finished step with a check, so you always know where you are. The version you are installing is printed at the bottom of that rail.

3.3 Step 2 — Requirements

The wizard inspects the server and shows the result in three blocks: your PHP version, the thirteen required extensions, and the five paths that must be writable. Everything must be green before Continue does anything.

Installer step 2: the requirements screen listing PHP version, thirteen extensions and five writable paths, all passing.
What to look at: the badge at the end of every row. OK / Loaded / Writable means that check passed. Any row that is not green is a change you must make on the server — the table in §14.1 lists the fix for each one.
Failing checkWhat to do
PHP version below 8.2Switch the PHP version of the domain in cPanel → MultiPHP Manager (or Select PHP Version), then reload the page.
An extension is missingEnable it in cPanel → MultiPHP Extensions. On a VPS, install the matching php8.x-* package and restart PHP-FPM.
A path is not writableSet the folder to 755 and make sure it belongs to the account that runs PHP. On a VPS: chown -R www-data:www-data storage bootstrap/cache.
.env is not writableSet the file to 644. If the file does not exist at all, the project root must be writable so it can be created.

3.4 Step 3 — License

Enter the purchase code or license key you received when you bought the product. If you bought on CodeCanyon, find it on your Envato Downloads page: click Download next to the item and choose License certificate & purchase code — a CodeCanyon code has the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. If you bought it somewhere else, use the license key from your account in that store; it may look different, and that is fine — paste it exactly as you received it. Press Activate; the wizard verifies the license and downloads the install data the next steps seed the panel from. The Continue button stays disabled until activation succeeds.

One license, one domain
A license activates one production domain. Local and staging hosts (localhost, 127.0.0.1, *.test, *.local) never count against it, so you can rehearse an installation freely.
Moving servers
Release the current domain first from Admin → License → Move to another domain, then activate on the new host. If the old installation is gone, contact support where you bought the product.
This step needs the internet
The server must be able to reach our license service over HTTPS. If activation reports a connection problem, try again in a few minutes — nothing is written until it succeeds.
Updating an existing installation? Updates never ask for your license mid-update. After updating you will find the new Admin → License screen with a friendly banner and a 14-day window to activate — the panel runs exactly as before either way; activation is what keeps future updates and add-on installs available.

3.5 Step 4 — Database

Enter the credentials of the empty database you created. Test connection opens a real connection and reports either success or the exact driver error; the Continue button stays disabled until that test passes, and the server checks the connection again before saving, so a copy-paste mistake cannot get through.

Installer step 3: database connection form with host, port, database name, username and password, showing a successful connection test.
What to look at: the green Connection successful banner above the form. Until it appears, only Test connection is clickable. On shared hosting the host is almost always localhost or 127.0.0.1 and the port 3306.
Host
localhost on nearly every shared host. Managed platforms sometimes give you a separate database hostname.
Port
3306 unless your host says otherwise.
Database name
The database you created. On cPanel it carries your account prefix, for example myaccount_redfamosa.
Username
The database user, also prefixed on cPanel.
Password
That user's password.

3.6 Step 5 — Application

Four values that define the panel itself.

Installer step 4: application settings with panel name, panel URL, base currency and timezone.
What to look at: the Panel URL field. Every link the panel ever generates — password resets, payment returns, webhook addresses — is built from this value, including its http:// or https:// prefix. Enter the address exactly as visitors will type it, with no trailing slash.
Panel name
Your brand. It appears in the header, the browser tab and outgoing e-mail.
Panel URL
The public address, with scheme and without a trailing slash, for example https://panel.example.com. Stored as APP_URL.
Base currency
The currency of the ledger — every balance, order cost and provider payment is recorded in it. Clients can display other currencies later (§8.4), but this one is the accounting currency.
Timezone
The server timezone written to APP_TIMEZONE. Reports and scheduled work follow it.
Choose the base currency carefully It is the unit the whole ledger is written in. Changing it after balances and orders exist does not convert anything — the old numbers keep their value and simply get read as a different currency. Decide now.

3.7 Step 6 — Admin account

Create the owner account: the super admin with unrestricted access to the panel.

Installer step 5: create the admin account with full name, email, password and confirmation.
What to look at: the hint under the password fields. The minimum is eight characters and the confirmation must match. This account cannot be recovered from the wizard afterwards — only through the normal password-reset e-mail, which needs working SMTP (§4.2). Save the credentials before you continue.

3.8 Step 7 — Install: Production or Demo

The last step asks one question: how much data should the panel start with. Pick a card and press Install now.

Installer step 6: choosing between Production and Demo seed data before pressing Install now.
What to look at: the two cards. The selected one is outlined — Production is preselected. This choice only affects the data that is inserted; the database structure is identical either way.
ModeWhat gets insertedChoose it when
Production Only the essentials: roles and permissions, the currency list, the language list with English and Spanish enabled, the platform taxonomy, the default settings, the starter reseller plans, your owner panel with its domain, and your admin account. The service catalogue is empty — you fill it from your own providers. Any real site. This is the recommended choice: nothing to clean up later, and no invented catalogue to confuse a customer.
Demo Everything above, plus a full sample business: example providers, dozens of categories and services, sample clients and staff, orders in every state including drip-feed, payment methods and bonus tiers, support tickets, blacklist entries, a second panel that acts as a reseller with its own clients and an active subscription, and a populated CRM with channels, labels, quick replies, AI agents and conversations. You want to explore the product with populated screens, or you are recording a demo. Not for a live site — the sample records are indistinguishable from real ones once you start working.
Use Production for a real panel There is no "remove the demo data" button. If you install with Demo and later decide to go live, the clean route is to drop the database, create an empty one and run the wizard again choosing Production. Deciding correctly now costs nothing; deciding later costs a reinstall.

While the progress bar runs, the wizard verifies the database once more, creates every table, seeds the data for the mode you chose, creates your admin user, checks the writable paths and finally writes the configuration file and the lock file. When it finishes you land on the confirmation screen.

If a step turns red

The failing step stops the run and prints the reason underneath. Fix the cause and press Try again: the wizard carries on from the step that failed, so nothing that already succeeded is repeated and you never have to start the wizard over. Every step is safe to run twice.

By far the most common cause is the database, and the code in the message says which one it is — the wording after it comes from your server, not from RedFamosa:

Message containsWhat it meansWhat to do
1045 Access denied The username or password is wrong, or the user has no rights on that database. Go back to the Database step and re-enter them. In cPanel, check the user is added to the database with all privileges.
1049 Unknown database The database name does not exist on that server. Create the database first, then go back and correct the name. On cPanel it is usually prefixed with your account name.
2002 Connection refused Nothing is listening: the MySQL service is down or on another port. Ask your host to start MySQL, or correct the port.
2002 Operation not permitted MySQL is running but the server is blocking PHP from reaching it — a firewall or, on CentOS/AlmaLinux/CloudLinux, SELinux. Try localhost instead of 127.0.0.1 in the Database step first: on most shared hosting that connects through the local socket instead of a TCP port that is often closed. On a VPS, your host can run setsebool -P httpd_can_network_connect_db 1. If the database is on a different server, add your web server's IP under Remote MySQL.
Telling your server apart from the panel All four of the above are answers from your server, which is why no setting inside RedFamosa can work around them. To confirm it in half a minute, put this in a file called dbtest.php next to index.php and open it in your browser — if it fails too, the fix belongs with your host. Delete the file afterwards.
<?php
try { new PDO('mysql:host=localhost;dbname=YOUR_DB','YOUR_USER','YOUR_PASS'); echo 'localhost OK'; }
catch (Exception $e) { echo 'localhost: '.$e->getMessage(); }
echo '<br>';
try { new PDO('mysql:host=127.0.0.1;dbname=YOUR_DB','YOUR_USER','YOUR_PASS'); echo '127.0.0.1 OK'; }
catch (Exception $e) { echo '127.0.0.1: '.$e->getMessage(); }
Installation complete screen with a link to the login page.
What to look at: every step in the rail is now checked. From here the wizard is closed for good: any later visit to /install redirects to the home page, because storage/app/installed.lock now exists.

What the wizard writes into .env

Only these sixteen values — everything else in the file keeps the documented default and can be changed by editing the file (§4.4):

KeyValue it writes
APP_NAMEThe panel name from step 4.
APP_URLThe panel URL from step 4.
APP_TIMEZONEThe timezone from step 4.
RF_DISPLAY_TIMEZONEThe timezone from step 4 as well. This one decides how dates are shown to your users, while APP_TIMEZONE decides how the server stores them. The wizard sets both to the same value, so the panel reads correctly from the first minute.
APP_ENV / APP_DEBUGAlways production and false.
DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORDThe connection from step 3.
DB_ENGINEInnoDB ROW_FORMAT=DYNAMIC — the storage engine every table is created with. Leave it alone. InnoDB is required, not preferred: a wallet credit runs inside a transaction that holds the row, so a payment notification delivered twice cannot pay twice, and MyISAM supports neither transactions nor row locks. If your host still defaults to MyISAM, an install without this stops at the first table with SQLSTATE[42000] … 1071 Specified key was too long. It only affects tables being created, so changing it on a working panel does nothing.
SESSION_DRIVERdatabase
CACHE_STOREfile
QUEUE_CONNECTIONdatabase
AUTOMATION_MODEmiddleware — the setting that works without any server configuration (§13).

APP_KEY is not written here: it was generated on the very first request, before the wizard even opened.

3.9 First login

Sign in with the e-mail and password from step 5 and you land in the admin dashboard. On a Production install every counter is zero — that is correct, the panel is waiting for you to fill it.

The admin dashboard right after a Production installation, with all counters at zero and the full navigation sidebar.
What to look at: the sidebar, which is the map of the whole product — Operation, Orders, Catalog, Finance and Users, with the CRM and reseller groups further down. The top bar carries the link to your public site, the language switcher, the light/dark toggle and your account menu.

A sensible order for the first hour:

  1. Add your upstream provider and sync its catalogue — §5.
  2. Review prices and hide the services you do not want to sell — §6.
  3. Configure SMTP so account and order e-mail actually leaves the server — §4.2.
  4. Enable at least one payment method — §8.2.
  5. Decide how background work runs and confirm it is running — §13.
  6. If you will sell reseller panels, set the reseller domain variables — §4.5.

3.10 Updating the script

Your panel tells you when there is one When a new version is released, staff who can apply updates see a notice with the version number and a one-line summary, and a button that opens Admin → System Update. Later keeps it quiet until the release after that; the System Update screen always shows it, so a dismissed notice can never hide a release from a colleague. Nothing extra contacts the internet for this — the licence check that already runs every few days carries the answer, and a panel that cannot reach the licence service simply never sees a notice.

There are two ways to move to a newer release. Back up your database and your .env file first either way — that step is never optional.

From the panel (recommended)

Admin → System Update applies a release package for you, and needs no SSH at all — it is the way to update on shared hosting.

  1. Open Admin → System Update. It shows the version you are running.
  2. Choose the release ZIP exactly as you downloaded it. Nothing is transferred yet — the screen just shows the file name and size.
  3. Press Upload package. The file travels in small 1 MB pieces with a live progress bar, so it goes through even on hostings with a strict upload limit — no php.ini changes needed. When the last piece arrives the server verifies the package and the update starts by itself; if anything is wrong (not a valid release ZIP, a lost piece, an expired session), the screen states the exact reason. Prefer FTP? Copy the ZIP into storage/app/updates/ instead — it then appears under Available packages with its version read from the package itself.
  4. Leave the page open. The update runs in four steps you can watch: the package is extracted and verified, the new code is applied (the replaced code is kept as a backup), the database is migrated, and the caches are cleared. When it finishes, the page reloads itself and confirms the new version.
Interrupted mid-update? If the browser is closed (or anything else interrupts the run) after the files were applied but before the database step finished, just open Admin → System Update again — the screen detects the half-applied update and completes it by itself. The /update assistant does the same from its own page.

The updater never touches your .env, your storage/, your uploads or your installed add-ons, and it refuses a package that is not newer than what you have. If a step fails, the screen offers Roll back to the previous version, which puts the previous code back.

Your translations are kept Languages you added in Admin → Languages, and any wording you corrected in the ones that ship with the product, survive an update whichever way you update. Two things make that true. The translation files are merged rather than replaced, so your wording stays and the new strings each release introduces are added next to it. And every time you press Save on that screen, a second copy is written into storage/ — the one folder no update ever touches — so even an upload that removed lang/ entirely is undone the moment the update finishes. (Releases before 1.7.0 replaced them. If that happened to you and you updated from inside the panel, your files are also inside the backup the update kept, under storage/app/updates/backup-<date>/lang/; copy them out before your next update, as only the most recent backup is kept.)
Uploaded images showing an old file? Installations made from an early package could serve stale images (profile pictures, blog covers, payment-method logos) no matter what was uploaded, because public/storage arrived as a real folder instead of the expected link. Updating repairs this automatically — your uploads are kept and start being served as soon as the update finishes. The browser-based /update assistant performs the same repair.

The package list keeps itself honest. A package whose version is already running shows an Installed badge instead of an Update button (an older one shows Older version), and every package has a delete button so you can clear it from the server once applied — no FTP needed.

Code backups. Each update keeps the code it replaced, and the screen lists those backups with the version they hold. Restore puts that code back — the escape hatch when you discover a problem after leaving the screen — and Delete frees the disk space. Restoring never touches your data, settings or uploads. To keep your disk from filling up, only the most recent backup is kept automatically after a successful update; older ones are removed for you.

Password-reset links in flight Updating to 1.2.0 clears any reset link that has been e-mailed but not yet used, because those links are now stored per panel. They expire after an hour in any case — anyone caught mid-recovery simply asks for a new one.

By hand, over FTP and SSH

Use this when your hosting will not let the updater write to the application folder, or when you simply prefer to do it yourself. It is the same result — you are performing by hand the steps the updater performs for you.

What the release ZIP contains. Unzipping it gives you two folders: redfamosa/ — the application itself — and Documentation/, which is this manual and never gets uploaded. Everything below refers to what is inside redfamosa/.

  1. Back up first. Export your database, and download a copy of your .env file. Nothing here is reversible without them.
  2. Put the site in maintenance mode if you can (php artisan down), so nobody places an order against half-replaced code. On hosting with no SSH, updating at your quietest hour is a reasonable substitute.
  3. Replace these folders and files — delete the old one on the server and upload the new one in its place. Overwriting without deleting first also works, but leaves behind files a release has removed:
    ReplaceWhy it matters
    app/ config/ database/ resources/ routes/ bootstrap/ The panel itself: code, default settings, migrations, screens.
    lang/
    upload over the top — never delete first
    The translations. This one folder is partly yours: languages you added and wording you corrected in Admin → Languages are files in here. Deleting it first removes them; uploading over the top keeps them and still delivers the strings this release introduces. If it has already happened to you, step 6 puts them back.
    vendor/ The one most often skipped. The bundled libraries. Leaving an old vendor/ against new code is the usual cause of a blank page or a “class not found” error right after an update.
    public/build/ public/fonts/ public/index.php public/.htaccess The compiled styles and scripts. Skip public/build/ and the new screens render unstyled.
    artisan composer.json composer.lock package.json package-lock.json vite.config.js changelog.txt .env.example Root files. .env.example is only a template — it is not your .env and does not overwrite it.
  4. Never touch these. They are yours, not the package's:
    Keep exactly as it isWhat you lose by replacing it
    .envYour database credentials and your APP_KEY. Replacing the key makes every encrypted value — provider keys, gateway secrets, messaging tokens — permanently unreadable.
    storage/Uploaded files, logs and installed.lock. Deleting the lock re-opens the installer on a live site.
    public/uploads/ and public/storage/ Your logo, avatars, blog images and payment receipts.
    addons/The add-ons you have installed.
  5. Empty the compiled cache. Delete every .php file inside bootstrap/cache/ (keep the folder itself). The package ships it empty; a stale config.php left there would go on serving the old settings.
  6. Update the database — this is the step that is never optional. Uploading files is only half of an update: the new code expects tables and columns that only the migrations create, and until they have run the panel cannot work. You have two ways to run them, and either is fine:
    WayWhat to do
    From your browser
    no SSH needed
    Open https://your-panel.com/update. The update assistant shows the version on the server, the version the database knows and exactly which changes are pending, and applies them with one button — migrations, translations, add-ons, caches and the background worker included. If you are signed in as an administrator it opens straight away; if you are not, it asks for the update key, a short code inside storage/app/update.key that you read with your File Manager or FTP client. The key changes after every successful update.
    From a shell php artisan migrate --force, then php artisan app:restore-languages (puts back any translation of yours the upload replaced — it says which, and does nothing when there is nothing to put back), then php artisan optimize:clear, and — only if you run a queue worker (automation mode queue, §13) — php artisan queue:restart. A worker is a long-lived process that keeps the old code in memory until it is told to stop.
    Every migration in RedFamosa is additive and safe to re-run, so this never destroys data.
  7. Bring the site back: php artisan up if you took it down.
  8. Check that it worked. Open Admin → System Update: the version at the top must be the one you just uploaded, and it appears in Update history marked Manual (FTP/SSH). If the number has not changed, the files did not land where you think they did — the usual cause is uploading a redfamosa/ sub-folder into the application root instead of its contents.
What your visitors see in between Between the moment the new files land and the moment the migrations run, the panel knows the two halves do not match. Rather than show broken pages it shows visitors a short “We are updating the panel” notice, and sends you — the administrator — straight to the update assistant. Signing in stays open the whole time, so a session that expired mid-upload is never a lock-out. The notice disappears by itself the second the database is up to date; there is nothing to switch off afterwards.
No SSH on your hosting? You can still update by hand: upload the files, then open https://your-panel.com/update and press the button. That assistant exists for exactly this case — shared hosting with a File Manager and no shell. If it is the upload size that blocks you rather than the shell, copy the ZIP into storage/app/updates/ over FTP and it appears in Admin → System Update ready to apply, which does the whole update for you.
Updating from 1.0.2 Version 1.0.2 has no System Update screen — it was added afterwards. So that one jump has to be made by hand, with the steps above, and the update assistant at /update arrives inside the files you upload: replace the folders, open /update, press the button. From this version on, Admin → System Update does everything for you and you never have to touch FTP again.
Never overwrite .env or storage/ .env holds your database credentials and your APP_KEY. Replacing the key makes every encrypted value in the database — provider API keys, gateway secrets, messaging tokens — permanently unreadable. storage/ holds uploaded files and installed.lock; deleting the lock file re-opens the installer on a live site.

Knowing which version you are on

Admin → System Update shows the installed version at the top, and below it an Update history of every version this installation has run: what it replaced, when, how it arrived and — when the update went through the panel — who applied it.

An update done by hand cannot announce itself: replacing files over FTP runs no application code. So the panel notices instead. The next time somebody opens the admin area, it compares the version in the code with the last one on record and, if they differ, writes the entry nobody wrote, marked Manual (FTP/SSH) and attributed to nobody — which is exactly what happened. The trail therefore stays complete whichever way you update.

One installation, one version Every reseller panel runs the same code as yours: they are areas of one installation, not separate copies. So the version on this screen is the version your resellers are on, and updating updates all of them at once. There is nothing to roll out panel by panel.

4. Configuration

RedFamosa is configured in two places, and knowing which is which saves a lot of searching:

WhereWhat lives thereApplies
Admin → Settings Everything an operator changes while running the business: branding, e-mail, payment methods, security options, CRM settings. Stored in the database, per panel — each reseller has their own copy. Immediately.
The .env file Everything the installation needs: database credentials, the panel address, the reseller-domain targets, tuning for background work and for the provider client. One file for the whole installation, no screen to edit it. On the next request. Restart the queue worker or scheduler if you use them.

4.1 General settings

Admin → Settings is a single screen with a row of tabs. Changes apply immediately — there is nothing to restart.

TabWhat you set there
GeneralSite title, support e-mail address, timezone and date format.
BrandingLogo (and which variant your public pages print — the dark-ink one, the light-ink one, or Both so it follows the visitor's light/dark choice), favicon, the panel's primary colour — the accent that drives the whole interface — and the console colours: the page background and the cards & sidebar, one colour each for light mode and for dark, every one with a live contrast reading and its own Reset. Saved per panel, so a reseller sets his own; the client portal is not affected.
SEOMeta title, description and the image used when your address is shared.
Social mediaThe profile links shown in the public footer.
AnalyticsGoogle Analytics, Tag Manager, Meta Pixel and a free-form snippet.
Social loginGoogle (including One Tap) and Facebook sign-in, with the app credentials.
Security & accessSee §4.3.
MaintenanceMaintenance mode with its own heading, message and a list of IPs still allowed in.
CurrencyThe base currency of this panel.
EmailSMTP credentials and the test send — §4.2.
Default settingOperational defaults: whether visitors may sign up, whether the service list is public, whether the API is enabled, rows per page, reCAPTCHA keys, the ticket limit per client and the average order time shown to clients.

Where things actually live

Not everything an operator thinks of as "a setting" is inside this screen. The frequent searches:

Looking forIt is here
Allow / block new client registrationSettings → Default setting
Base currency of the panelSettings → Currency
Exchange rates and the list of currenciesAdmin → Currencies (§8.4)
Default language, adding a languageAdmin → Languages — a reseller picks which of them their own storefront offers in their Settings → Languages (§9.10)
Payment methods and their keysAdmin → Payments (§8.2)
Top-up bonus tiersAdmin → Payment Bonuses (§8.3)
Blocked IPs, links and e-mailsAdmin → Blacklist
Outgoing order webhooksAdmin → Webhooks (§11.5)
How background work runs, and when it last ranAdmin → Cron Jobs (§13)
The public address of the panelAPP_URL in .env (§4.4) — there is no admin field for it
Minimum and maximum order quantityPer service, in Admin → Services — there is no global minimum
Every panel has its own settings These settings belong to the panel you are signed in to. A reseller opens the same screen inside their own panel and sets their own title, logo, colour, currency and SMTP — which is what makes a child panel look like a different product rather than a copy of yours.

4.2 Email (SMTP)

The panel sends account e-mail, password resets, payment confirmations, ticket replies and the reseller billing notices. Until SMTP is configured, none of that leaves the server. Open Settings → Email:

Provider preset
Picks the usual host and port for a common mail provider so you only fill in the credentials.
SMTP host
Your provider's outgoing server.
SMTP port
587 with TLS or 465 with SSL. Avoid port 25 — shared hosts usually block it.
Encryption
tls, ssl or none. Match it to the port.
Username / password
Normally the full mailbox address and its password. The password is write-only: leave the field blank to keep what is stored.
From address / from name
What your customers see as the sender. Many providers reject mail whose sender is not the authenticated account. On a reseller's panel the display name is replaced by that reseller's own panel name, so their customers never read your brand in their inbox; the address is always this one, because it is the mailbox your server authenticates as and changing it per panel would send the message to spam.
Welcome e-mail
Whether a new client gets a welcome message on registration.

Use Send test before you save your work as done: it sends a real message to the address you type, using the values currently on screen, and reports the exact SMTP error if the server refuses.

Two-factor accounts need an app password If the mailbox is protected by two-factor authentication — which is normal for the big providers — generate a dedicated app password for SMTP. The account's login password will always be rejected.

4.3 Security

Settings → Security & access holds four controls:

Admin IP allowlist
Restrict the admin area to specific addresses. Powerful, and easy to lock yourself out with — add your own current address first, and be sure it is static.
Session lifetime
Minutes before an idle session expires, from 5 minutes to 30 days.
Force staff two-factor
When on, every staff and admin account must set up two-factor authentication before it can work. Clients can always enable it for themselves from their own profile.
Minimum password length
Between 6 and 72 characters, applied to new passwords and to changes.

Two more protections are always on and need no configuration: failed sign-ins are throttled automatically (five attempts per minute for a given account and address), and every form is protected against cross-site request forgery.

Forcing HTTPS is your web server's job There is no "force HTTPS" switch in the panel: redirecting http:// to https:// belongs in your web server or in .htaccess, where it is more reliable. What the panel does need is a matching APP_URL and SESSION_SECURE_COOKIE=true once you are on HTTPS — see §4.4.

4.4 The .env file

.env sits in the project root, next to artisan — one level above public/. It is created for you on the first request by copying the fully commented .env.example that ships with the product, and the installer then fills in fifteen values (§3.8). Every other setting keeps its documented default until you edit the file.

This is the part of the product that has no user interface, so it is worth knowing what is in there. Open it with the cPanel File Manager editor (enable "show hidden files" — the name starts with a dot) or over SSH. The file is organised in commented blocks:

BlockContainsTouch it when
ApplicationAPP_NAME, APP_KEY, APP_ENV, APP_DEBUG, APP_URL, locales, APP_TIMEZONEYou move the panel to another address or change its name.
DatabaseDB_*You move the database to another server.
Sessions, cache and queueSESSION_DRIVER, SESSION_LIFETIME, SESSION_SECURE_COOKIE, SESSION_DOMAIN, CACHE_STORE, QUEUE_CONNECTIONYou add Redis, or you serve resellers from sub-domains of one domain.
Background workAUTOMATION_*See §4.6.
E-mailMAIL_*You prefer to set SMTP in the file instead of in Settings.
Panel branding and reseller domainsRF_BRAND_PRIMARY, RF_PANEL_*, RF_DISPLAY_TIMEZONE, RF_ROUNDING_DECIMALS, RF_DEFAULT_MARKUPSee §4.5.
Upstream providersRF_PROVIDER_*, RF_BREAKER_*, dispatch and polling batchesSee §4.7.
Public APIRF_API_*, RF_WEBHOOK_*See §4.7 and §11.
CRM and messagingCRM_*You use the omnichannel inbox (§12).
Social sign-inGOOGLE_*, FACEBOOK_*You want "continue with" buttons on the sign-in page.
LoggingLOG_CHANNEL, LOG_STACK, LOG_DAILY_DAYS, LOG_LEVELYou are diagnosing a problem. Put LOG_LEVEL back to error afterwards.
RedisREDIS_*You actually have a Redis server.
Demo modeRF_DEMO_MODESee §4.8.

The five values that matter most

APP_URL
The address of the panel. Every link the panel produces is built from it, including the scheme — see the warning below.
APP_KEY
The encryption key. Provider API keys, gateway secrets and channel tokens in the database are encrypted with it. Back it up with the database and never change it on a live panel.
APP_DEBUG
Must stay false on a public site. Set to true and any visitor who triggers an error sees file paths and configuration.
SESSION_SECURE_COOKIE
true only when the site really is on HTTPS. Set it on a plain-HTTP site and nobody can stay signed in, because the browser never sends the cookie back.
AUTOMATION_MODE
How background work runs. The whole of §13 is about this one line.
The scheme in APP_URL is not cosmetic The panel generates every link, form action and redirect with the scheme written in APP_URL. If it starts with https:// while the site is still served over plain http://, the wizard's own buttons and every internal link point at an address the server does not answer, and the panel looks broken. Use https:// only once the certificate is really installed. See §14.1.
.env must never be reachable from the web It holds your database password and your encryption key. Because the document root points at public/, the file sits outside it and cannot be requested — which is another reason the document root must be set correctly. After editing the file, run php artisan optimize:clear if you have SSH, or simply reload the site.

4.5 Reseller domains: the variables with no screen

These three lines are what turn reseller panels (§9) from a concept into a working feature. There is no admin screen for them — they can only be set in .env, and they ship empty on purpose, because promising an address your hosting does not actually serve is worse than not offering the option.

VariableWhat it enablesExample
RF_PANEL_BASE_HOST Free sub-domains for your resellers. With panels.example.com, a reseller called max is served at max.panels.example.com. Leave it empty and the admin refuses to assign sub-domains at all — it tells you to set this variable first. panels.example.com
RF_PANEL_IP The A record target a reseller publishes to point their own domain at your server. Needed for apex domains such as example.com, which cannot use a CNAME. 203.0.113.10
RF_PANEL_CNAME_TARGET The CNAME target for the same purpose, normally the host name of your own panel. Empty means the CNAME route is simply not offered. panel.example.com

Both DNS targets are printed to the reseller on their own Domains screen, so fill in at least one — otherwise the reseller is told to point their domain somewhere and is given nothing to point it at.

The sub-domain feature needs wildcard DNS RF_PANEL_BASE_HOST only works if a wildcard DNS record (*.panels.example.com) resolves to this server and the web server answers for that wildcard with the same document root as the main panel. On cPanel that means a wildcard sub-domain; on a VPS, a server_name *.panels.example.com block. A wildcard TLS certificate is what keeps those sub-domains on HTTPS. RedFamosa never issues certificates — that is your hosting's job.

Two more lines in the same block are worth knowing:

RF_BRAND_PRIMARY
The accent colour used when a panel has not chosen its own. Each panel can override it with its own primary colour.
RF_DISPLAY_TIMEZONE
The timezone used to group dashboard and report figures by day when APP_TIMEZONE is left at UTC. Set it to the timezone you actually think in, or your "today" will not line up with your working day.

4.6 Background work

AUTOMATION_MODE chooses how recurring work runs; the intervals below choose how often. The three modes and how to set each one up are covered in §13.

VariableShipped valueMeaning
AUTOMATION_MODEmiddlewaremiddleware, queue or scheduler. See §13.1.
AUTOMATION_THROTTLE60Minimum seconds between two runs of the same recurring task.
AUTOMATION_SYNC_INTERVAL3600Seconds between automatic provider catalogue syncs.
AUTOMATION_SCRAPE_INTERVAL86400Seconds between metadata scrapes of a provider's public page. Deliberately slow: one provider per tick.
AUTOMATION_AVGTIME_INTERVAL3600Seconds between recalculations of average delivery times.
AUTOMATION_SUBSCRIPTIONS_INTERVAL3600Seconds between reseller-plan billing passes when there is no real cron (§10.7).
AUTOMATION_MAX_JOBS25Most jobs a single piggybacked pass may run, so a visitor's page never hangs.
AUTOMATION_MAX_SECONDS10Hard time limit for that same pass.
The mode can also be switched from the admin Admin → Cron Jobs lets you change the automation mode without editing files; that choice is stored in the database and takes precedence, which makes the .env line the installation default rather than the last word. The intervals above are read from .env only.

4.7 Providers, orders and the public API

Sensible defaults ship for all of these. Change them when a provider is slow, when you run a high volume of orders, or when you want the public API to be stricter.

Talking to upstream providers

VariableShipped valueMeaning
RF_PROVIDER_TIMEOUT30Seconds to wait for a provider's API before giving up.
RF_PROVIDER_CONNECT_TIMEOUT5Seconds allowed just to open the connection — a separate budget from the one above. If one provider reports cURL error 28: Connection timed out after 5002 milliseconds while your others work, your server needs longer to resolve DNS or complete the TLS handshake: raise this to 10 or 15 and check the balance again.
RF_PROVIDER_RETRIES2Retries after a failed call.
RF_PROVIDER_RETRY_DELAY_MS500Milliseconds to wait between those retries.
RF_BREAKER_THRESHOLD5Consecutive failures before the circuit breaker opens and the provider is paused.
RF_BREAKER_COOLDOWN300Seconds a tripped provider stays paused before it is tried again.
RF_CATALOG_CACHE_MINUTES60How long a fetched provider catalogue is cached.
RF_PROVIDER_PROXYemptyOptional outbound proxy for provider API calls. Leave empty unless a provider is unreachable from your server.
RF_PROVIDER_PROXY_BYPASSemptyProviders that keep being contacted directly even when the proxy is set (comma separated).
When you would need the proxy Very rarely. It is for the case where one provider times out from your server while the rest of the internet works fine — that is a network block at your hosting, not a fault of the provider or of this panel, and the first thing to do is open a ticket with your host. Until they fix it, pointing RF_PROVIDER_PROXY at a proxy you control keeps that provider working. Use RF_PROVIDER_PROXY_BYPASS to list the providers that were always reachable, so only the blocked one goes through the proxy and your other providers' API keys never travel through it.
Why the circuit breaker matters Without it, one dead provider makes every dispatch pass wait for its timeout, and a queue of orders for healthy providers stalls behind it. After RF_BREAKER_THRESHOLD consecutive failures the panel stops calling that provider for RF_BREAKER_COOLDOWN seconds and keeps working on the rest.

Order pipeline

VariableShipped valueMeaning
RF_DISPATCH_BATCH25Orders sent upstream per dispatch pass.
RF_MULTISTATUS_BATCH100Orders queried per multi-status call.
RF_POLL_MIN_SECONDS / RF_POLL_MAX_SECONDS60 / 3600The window the panel spaces status checks across: fresh orders are polled often, long-running ones progressively less.
RF_REFILL_WINDOW_DAYS30Days a client may request a refill after an order completes (§7.4).
RF_DEFAULT_MARKUP20Percentage added over a provider's cost when importing a new service.
RF_ROUNDING_DECIMALS4Decimal places kept for prices. Per-unit amounts are tiny, so 4 is the safe default.

Public API and outgoing webhooks

VariableShipped valueMeaning
RF_API_RATE_LIMIT120Requests per minute per API key. 0 removes the limit (§11.4).
RF_API_IDEMPOTENCY_TTL1440 = 24 hMinutes a repeated add request carrying the same Idempotency-Key replays the original answer instead of creating a second order (§11.3).
RF_WEBHOOK_TIMEOUT10Seconds the panel waits for your webhook endpoint to answer.
RF_WEBHOOK_MAX_ATTEMPTS5Delivery attempts before a webhook is abandoned (§11.5).

CRM

VariableShipped valueMeaning
CRM_META_GRAPH_VERSIONv21.0Graph API version used for WhatsApp, Messenger and Instagram. Change it only when Meta asks you to.
CRM_EMAIL_INTERVAL60Seconds between mailbox polls for e-mail channels.
CRM_TOKEN_HEALTH_INTERVAL3600Seconds between channel-token health checks.
CRM_TOKEN_WARN_DAYS / CRM_TOKEN_EXTEND_DAYS / CRM_TOKEN_IG_REFRESH_DAYS14 / 21 / 30How many days ahead of expiry the panel warns you, tries to extend a token, and refreshes an Instagram token.
CRM_INBOX_PAGE_SIZE25Conversations per page in the inbox.
CRM_MEDIA_DISKpublicWhere inbound attachments are stored.

4.8 Demo mode

RF_DEMO_MODE=false ships in the file and should stay that way on any real site. Setting it to true does three specific things, all aimed at a public demo you want strangers to walk into:

That last one is the difference between a demo you can leave online and one you cannot. A demo hands the admin panel to whoever opens it, and most of what a visitor can do there lives in the database — so restoring your demo database on a schedule puts everything back. Files are the exception: an uploaded add-on is PHP that keeps running, and an edited lang/<code>.json repaints every screen for good. No database restore undoes either. Everything else stays fully usable, which is the point: settings, orders, tickets, and enabling or disabling an add-on all work normally, so a visitor still sees the real product. Your own maintenance is unaffected — commands you run over SSH (updates, migrations, php artisan app:restore-languages) are never blocked by this.

Never enable demo mode on a panel with real customers It is a shortcut past the front door. It does not reset your data and it does not hide money — it makes signing in easier and it stops a handful of file-writing actions, nothing more. Keep it false unless the site exists purely to be looked at. Note that demo mode is unrelated to the Demo data option in the installer (§3.8), which only chooses what gets seeded.

5. Providers (upstream SMM)

A provider is the wholesale SMM service you buy from. Your panel is the shop window; the provider does the actual delivery. RedFamosa talks to any provider that implements the standard SMM API — the key + action format, widely known as the Perfect Panel API, that virtually the whole industry has settled on.

5.1 The provider protocol

Every call is a single request carrying your API key and the name of the action:

POST https://provider.example.com/api/v2
Content-Type: application/x-www-form-urlencoded

key=YOUR_API_KEY&action=ACTION[&...additional parameters]

RedFamosa uses these actions against your providers:

ActionWhen RedFamosa calls it
servicesWhen you sync a catalogue — it returns the provider's full service list with IDs, names, rates and limits.
addWhen an order is dispatched. The provider answers with its own order ID, which the panel stores.
statusTo follow a single order.
multi-statusTo follow many orders in one call — the efficient path used by the polling pass.
balanceTo read your credit at the provider, and as the connection test.
refillWhen a refill is requested for an order that lost quantity.
cancelWhen a cancellation is requested for an order not yet delivered.
Not every provider implements everything services, add, status and balance are universal. Refill and cancel are per-service capabilities: the catalogue sync reads those flags from the provider and only the services that really support them offer the buttons to your clients.

5.2 Adding a provider

Go to Admin → Providers → Add provider:

Name
An internal label. Your clients never see it.
API URL
The endpoint the provider gave you, usually ending in /api/v2.
API key
Your key from the provider's dashboard. Stored encrypted and never shown again in full after saving.
Default markup %
The margin added over the provider's rate when services are imported. 40 means you sell at 1.4× the wholesale price. Individual services can be re-priced afterwards.
Status
Active or inactive. An inactive provider is never polled and never receives orders.

After saving, open the row's menu and run Test connection. It calls the provider's balance action, so a success proves both that the URL is right and that the key is accepted.

5.3 Syncing the catalogue

Sync services on the provider row imports the provider's list into your catalogue. The dialog controls exactly what the sync is allowed to touch:

Markup %
Overrides the provider's default markup for this run.
Scope
This provider only, or every provider at once.
Fields to sync
Which fields may be overwritten on services you already imported: cost, sale price, status, name, min/max, drip-feed flag, cancel flag. Untick anything you have customised locally — this is how you keep your own names and prices through a re-sync.
Bulk import limit
How many new services to bring in. A first sync of a large provider can add thousands of rows; importing in batches keeps the catalogue reviewable.
Currency rate
An optional multiplier when the provider quotes in a different currency from your base currency.
Categories are created for you Imported services bring their category name with them. The panel creates the category if it does not exist and attaches it to the matching platform by name, so two providers that both sell "Instagram Followers" land in one category rather than two.
Review prices before you open the shop A markup is a blunt instrument: it applies the same percentage to a service costing a fraction of a cent and to one costing several units of currency. After the first sync, sort the catalogue by price and sanity-check both ends before you let clients order (§6).

5.4 Balance, metadata and reliability

Provider balance

The provider list shows your current credit at each active provider, refreshed by the background worker. That is your buying power: when it runs out, orders start failing at the provider even though your own clients paid you. Top it up with the provider directly, and keep an eye on the figure — it is the single most common cause of a panel that "stopped delivering".

Metadata scraping

Many providers publish a public services page with descriptions and average delivery times that their API does not return. Enable scrape metadata on a provider and give it that public URL: a background task reads the page on a slow schedule — one provider per tick, at most once per AUTOMATION_SCRAPE_INTERVAL — and fills in the description and average time that your clients then see on the order form. The fetch is capped in time and size and is not allowed to reach private addresses.

When a provider misbehaves

Calls carry a timeout and a small number of retries, and a circuit breaker sits in front of every provider: after a run of consecutive failures the panel stops calling it for a cooldown period and moves on to the others, so one broken provider cannot hold up everybody else's orders. The thresholds are in §4.7.

Credentials are encrypted at rest Provider API keys, payment gateway secrets and messaging tokens are encrypted before they are stored, using your installation's APP_KEY. They are never returned by the API and never written to the log. This is also why replacing APP_KEY on a live panel is destructive: nothing already encrypted can be read back.

6. Catalogue

The catalogue is the three-level structure your clients browse: platforms → categories → services. Providers fill it; you curate it.

6.1 Platforms

Platforms are the social networks — Instagram, TikTok, YouTube, Facebook, X, Telegram and so on. RedFamosa ships with twenty-four definitions plus an Other catch-all, created the moment the panel is installed. They do two jobs: they group categories in the admin, and they power the network selector at the top of the client order form.

You do not create platforms by hand. When a category is imported, the panel infers its platform from the category name; anything it cannot place lands in Other, where you can reassign it.

6.2 Categories

Categories sit under a platform and group related services — Instagram Followers, TikTok Views. They are created automatically during a catalogue sync and can be renamed, reassigned or reordered in Admin → Categories. A category name is shared: the same name coming from two different providers is one category, not two, which is what lets you sell the same product from several sources without duplicating your shop.

6.3 Services

A service is what a client actually buys. Manage them in Admin → Services.

FieldWhat it does
NameThe name clients see. Yours to rewrite — provider names are often unreadable.
CategoryWhere it appears in the storefront.
TypeHow the order is submitted upstream. See the list below.
Cost per 1,000What the provider charges you. Filled by the sync.
Rate per 1,000What you charge. Cost plus your markup; edit it freely.
Min / MaxThe quantity range accepted. Orders outside it are refused at creation.
Provider & provider service IDWhich upstream account fulfils it, and under which ID.
RefillWhether clients may request a refill, within the guarantee window.
CancelWhether clients may request a cancellation.
Drip-feedWhether the service may be delivered in scheduled instalments (§7.4).
Average timeThe delivery estimate shown on the order form, from the metadata scrape or typed by you.
DescriptionThe detail panel on the order form: what is delivered, quality, guarantees.
StatusActive (orderable) or hidden (kept, with its order history, but out of the storefront).

Service types

The type decides which extra fields the order form asks for and how the order is sent upstream. It is set by the sync from the provider's own type and can be corrected by hand.

TypeWhat the client provides
DefaultA link and a quantity. Almost everything you sell is this type.
PackageA link only — the quantity is fixed by the package.
Custom comments / custom comments packageA link and the list of comments to post, one per line. The quantity is the number of lines.
Mentions (custom list, hashtag, user followers, media likers, with hashtags)A link plus the source of the people to mention: a list of usernames, a hashtag, another account's followers, or the accounts that liked a given post.
Comment likesA link to the post and the username whose comment should receive the likes.
SubscriptionsA profile link plus the rules for future posts: how many units per new post, and the range of posts to cover.
Drip-feed is not a type It is a flag any ordinary service can carry. A drip-feed service is still ordered normally; the client simply chooses to receive it in instalments (§7.4).

6.4 Bulk management

A first sync can bring in thousands of services, so the services screen is built for volume. Group the list by category, select rows individually, a whole category, or everything that matches the current filter, and apply one action:

Sell less, better A catalogue of four thousand near-identical services converts worse than eighty you can stand behind. Import everything, then hide what you have not tested. Hidden services keep their prices and come back with one click.

7. Orders

An order is a quantity of one service against one target link. From creation to completion the whole pipeline runs in the background: the panel dispatches to the provider, follows the order and reports what the provider actually says.

7.1 Placing an order

  1. The client picks a network from the grid at the top of the form.
  2. Then a category, then a service. The form shows the description, the average delivery time, the accepted quantity range and the price per 1,000.
  3. They paste the link and type the quantity; the charge is calculated as they type.
  4. Optionally they leave a Note for the team — delivery instructions or a reference of their own, up to 500 characters. It is stored with the order and changes nothing about what is delivered; your staff read and edit it in §7.5, and the client sees it again under Order details in their own Orders list.
  5. Place order validates in one pass: the link and their account against the blacklists, the quantity against the service range, and the charge against their balance.

Clicking the category field once a category is chosen reopens the list with the current choice still shown and ticked, so a client can switch to another one without losing their place; the list has its own search box inside. The × beside the chosen category is what clears it, and it also returns the whole catalogue — use it when a client picked a network first and wants to browse everything again. This is the same in all client designs.

The charge leaves the wallet the moment the order is created — that is what an order in awaiting state means — and the background worker picks it up for dispatch on the next pass.

7.2 Bulk order

Clients who work with many links use the Bulk order tab. One order per line, three values separated by pipes:

service_id|link|quantity
143|https://example.com/profile/one|1000
143|https://example.com/profile/two|2500
207|https://example.com/post/abc|500

Every line is parsed before anything is created. A line with the wrong number of fields, a non-numeric service or quantity, or an empty link is reported with its line number and skipped — the valid lines still go through, so one typo does not cost the whole batch.

Three fields, no more Drip-feed values are not accepted in bulk. A line with a fourth field is rejected as a format error. Drip-feed orders are placed one at a time on the normal form, or through the public API.

7.3 Order states

These are the states an order moves through in the admin. The rule the whole pipeline is built around: an order only becomes Completed when the provider says so.

StateMeaning
AwaitingCreated and paid for; waiting for the next dispatch pass. Nothing has been sent upstream yet.
PendingAccepted by the provider, which returned its own order ID. The panel is now polling it.
ProcessingThe provider reports it is working on the order.
In progressDelivery has started.
ActiveA drip-feed parent whose instalments are still being released.
CompletedThe provider confirmed full delivery.
Part of the quantity was delivered and the rest will not be. The undelivered portion is refunded to the wallet.
CanceledCancelled before delivery. The charge is credited back to the client's wallet.
RefundedRefunded by an administrator after the fact.
ErrorThe provider rejected the order, or the service no longer exists. The provider's own message is stored on the order so you can see why.
SimulatedA manual marker meaning "this was never really sent upstream". Only an administrator can set it.
An error is not a refund When a provider rejects an order the panel does not silently return the money: it parks the order in Error with the provider's message attached, because the right answer is often to fix the service and retry rather than to refund. Deciding is your job — open the order and either retry it or cancel it, which is the action that credits the wallet. Watch the Error filter: a pile of them usually means an exhausted provider balance or a service ID that changed upstream.

The public API speaks a slightly different vocabulary, because API clients expect the standard SMM status names. The panel translates on the way out:

Internal stateReturned by the API
Awaiting, Pending, SimulatedPending
ProcessingProcessing
In progressIn progress
ActiveActive
CompletedCompleted
PartialPartial
Canceled, ErrorCanceled
RefundedRefunded

7.4 Refill, cancel and drip-feed

Refill

When a service has refill enabled and the delivered count later drops, the client gets a Refill button on the order row for as long as the guarantee window lasts (RF_REFILL_WINDOW_DAYS, thirty days as shipped). The request is forwarded to the provider and its refill reference is stored, so the refill can be followed separately from the original order.

Cancel

When a service has cancel enabled, a client — or you, from the admin — can ask for cancellation while the order is still undelivered. The request goes upstream; on confirmation the charge is credited back to the wallet as a new ledger entry. Cancellations that the client asks for arrive in Admin → Cancel Requests.

Drip-feed

Drip-feed spreads a delivery over time instead of dropping it all at once — the natural way to grow an account. On a drip-feed service the client sets:

Quantity
How many units each run delivers.
Runs
How many times to repeat it.
Interval
Minutes between runs, from 1 minute to 1,440 (24 hours).

The client pays the full amount once, when the parent order is created. From then on the background worker releases one child order per interval, each drawing its share from the amount already paid — no further debit is ever taken. The parent stays Active and shows overall progress; it becomes Completed when the last run has been released. Everything drip-feed related is listed under Admin → Drip-feed.

Drip-feed needs the background worker Runs are released by the same machinery that dispatches and polls orders. In middleware mode that machinery only moves when someone visits the site, so a panel with no traffic at 3 a.m. releases its 3 a.m. run late. If you sell drip-feed seriously, use scheduler or queue mode (§13).

7.5 Admin order management

Admin → Orders lists every order in your panel with filters by status, service, provider, client and date. From a row you can open the order, read the provider's raw response, retry a dispatch, and edit the status by hand.

The edit window carries two separate note boxes and they are not interchangeable. Note / API response is the operational one: your provider's last answer is written there automatically, and a successful dispatch clears it. Client note is what the customer typed on the order form (§7.1); nothing automatic ever touches it, and you may edit it to record what was agreed. The client sees the latter under Order details.

Editing a status moves money only in one direction Setting an order to Canceled credits the client's wallet. Every other manual status change is a label change: it does not touch the balance. Use Simulated honestly — it exists to record that an order never reached a provider, not to make a failure look finished.

8. Billing

RedFamosa is prepaid. A client adds funds, and orders spend from that balance. Every movement — deposit, bonus, order charge, refund, adjustment, reseller margin, plan charge — is a line in a ledger.

8.1 The ledger

There is no editable "balance" column that could drift out of step with reality. A balance is the sum of that wallet's ledger entries, and entries are only ever appended — never edited, never deleted. That single decision is what makes the money in the panel auditable: you can always answer "where did this figure come from" by reading the lines.

Entry typeCreated when
depositA top-up is confirmed, manually or by a gateway.
bonusA top-up qualifies for a bonus tier (§8.3).
order_debitAn order is created.
order_refundAn order is cancelled or partially delivered.
reseller_cost_debitA reseller's client orders: the reseller pays you your price.
reseller_marginThe difference the reseller keeps on that same order.
manual_adjustAn administrator corrects a balance, with a reason.
chargebackA confirmed payment is reversed.
plan_charge / plan_refundA reseller subscription is billed or refunded (§10).

You read the ledger in Admin → Transaction Logs; clients see their own under Transactions in their area. To correct a balance, do not look for an editable field: use the balance adjustment on the user, which writes a manual_adjust entry with your reason attached.

8.2 Add Funds and payment methods

Payment methods live in Admin → Payments, where each one is a row you create, configure and enable. Three are available:

Not sure what a field wants? Tap the (i) Every credential on the connect form has its name above the box and a small (i) beside it. Tap that and a sentence tells you what the value is and where to find it in your gateway's dashboard — which key starts with pk_ and which with sk_, what live and sandbox do, and so on. Gateways you install as add-ons describe their own fields the same way. When you edit a method that already has credentials saved, every box opens empty on purpose and reads Leave blank to keep the current value: fill in only the ones you want to replace.

Fees: who pays them

Every payment method has a Fee % and a Fixed fee, and next to them a Who pays the fee setting that decides what those numbers actually do. Take a 10.00 deposit with a 1.00 fee:

Do not charge it
The customer pays 10.00 and their balance grows by 10.00. You absorb the fee — your gateway still takes its own cut out of what reaches you. Every method that existed before this release is set to this, so updating never changes what your customers pay.
Add it on top
The customer is charged 11.00 and their balance grows by 10.00. This is the usual choice: the customer asks for 10.00 of credit and gets 10.00, and the processing cost does not come out of your margin.
Take it from the balance
The customer is charged 10.00 and their balance grows by 9.00. Use this when you want the amount your customer pays to stay a round figure.

A second setting, How the client sees it, decides whether the customer sees the fee as its own line before paying (Itemised) or only the final amounts (Included). Either way they always see the total they are about to be charged, and the deposit receipt lists Deposit, Payment fee, Total paid and Total credited separately.

Fixed fee is in your base currency The percentage applies to whatever the customer is depositing; the fixed part is configured in your base currency and converted for a customer paying in another one. If a method shows “Fee not charged” in the list, it has a fee configured that nobody is paying — open it and choose who does.

Manual / bank transfer

The client picks the manual method, enters an amount, uploads a proof of payment and submits. The deposit waits for you: you review it in the admin and approve or reject it, and approval is what credits the wallet (plus any bonus). You configure the instructions shown before the upload — bank details, a QR image, whatever your customers need — and you can attach an image to them.

Start here Manual is the method to enable on day one: it needs no account anywhere, works in every country, and lets you take real money while a gateway application is still being reviewed.

Stripe

Card payments through Stripe Checkout.

Publishable key
From your Stripe dashboard, under developer API keys.
Secret key
Stored encrypted; used server-side only.
Webhook secret
From the webhook endpoint you create in Stripe. It is what proves an incoming payment notification really came from Stripe.
Mode
Sandbox while you test, live when you are ready.

In your Stripe dashboard, add an endpoint pointing at {APP_URL}/api/webhooks/stripe.

PayPal

Client ID
From your PayPal REST application.
Secret
Stored encrypted; used server-side for the token exchange.
Webhook ID
The ID PayPal assigns to the webhook you create, used to verify notifications.
Mode
Sandbox or live.

The PayPal webhook endpoint is {APP_URL}/api/webhooks/paypal.

Test with a real, small payment Sandbox proves the keys are valid; it does not prove that your live account, currency and webhook are wired together. Before you announce the panel, switch to live and send yourself one minimal top-up. If the wallet does not move, the webhook is the first thing to check.

8.3 Payment bonuses

Admin → Payment Bonuses is where you reward bigger top-ups. A tier is three things: the payment method it applies to, the amount from which it applies, and the percentage to add.

Bank transfer   from  50   +5 %     a 50 top-up credits 52.50
Bank transfer   from 100  +10 %     a 100 top-up credits 110.00

The bonus is granted when the deposit is confirmed — on approval for a manual payment, on the gateway's confirmation otherwise — and it is written as its own bonus ledger entry, so the deposit and the gift you added to it never blur together in the accounts.

8.4 Multi-currency

The base currency you chose during installation is the currency of the ledger: every balance, charge and refund is recorded in it, and it is the only one that matters accounting-wise. Clients may nonetheless read the panel in another currency: they pick a display currency in their profile and every amount is converted for display only.

Admin → Currencies is where the list lives. Each currency carries a rate to base: how much one unit of that currency is worth in your base currency. Your base currency is pinned at 1 and cannot be switched off once there are ledger entries.

RedFamosa ships approximate rates so a fresh install is usable on day one, expressed against whichever currency you picked in the installer — install in dong or in naira and the whole table is quoted in dong or in naira, with your own currency sitting at 1. From there you have two ways to keep them honest, and you choose per currency.

Automatic. Switch Auto update on for a currency and the panel refreshes its rate from a public market feed every six hours — no API key, no account, no configuration. The Updated column states when each rate last moved, and Update rates now fetches immediately if you would rather not wait. If the feed cannot be reached the panel changes nothing and says so; a stale rate is safer than a wrong one, and a currency the feed does not quote simply keeps the rate you set by hand.

Manual. Leave Auto update off and your number is final. That is the right choice when you deliberately sell at a rate of your own, or price in a currency no feed quotes. Your base currency is never auto-updated: it is pinned at 1, because it is the unit everything else is measured against.

Exchange margin. The single percentage above the table is applied on top of every automatic rate, in your favour. At 2 %, a client paying in that currency pays about two percent above the market rate — the cushion that covers the gap between the moment they top up and the moment you pay your provider. Leave it at 0 to track the market exactly.

Why this matters more than it looks A rate you set once and forget is a slow leak. If a currency loses a fifth of its value while the panel still uses last quarter's number, every top-up in it credits noticeably more balance than the money you actually received — and you find out from your margins, months later. Either turn Auto update on, or put "review the exchange rates" on your own calendar.

Currencies that have no cents

Some currencies have no fractional unit at all — the Vietnamese dong, the Japanese yen, the Korean won, the Indonesian rupiah, the Chilean peso. There is no such thing as half a dong, so those are always written as whole numbers: ₫128,205, never ₫128,205.13. The ones listed above already know it. If you add such a currency yourself, tick This currency has no cents on it.

It is deliberately a yes/no and not a number of places. How many decimals your panel shows is one choice for the whole installation and lives in Settings → Currency; whether a currency has any is a fact about the money, and that is the only thing this box states. Every currency that does have cents keeps following your global setting exactly as before.

Currencies whose symbol goes after the amount

Most currencies lead with their symbol the way the dollar does ($100), and a handful trail it: Vietnamese is written 100₫, Polish 100 zł. Tick The symbol goes after the amount on a currency and every figure your panel prints in it — wallets, prices, orders, invoices, the customer portal — follows. The Reads as column in the list shows you exactly how each currency will look, so you can check the whole table at a glance.

Five currencies that ship with the product already trail their symbol: VND, PLN, RON, RUB and UAH. If you are updating from an earlier version these are corrected for you; every other currency keeps the leading symbol it has always had, and a currency whose symbol you edited yourself is never changed. The euro is deliberately left leading, because both €100 and 100 € are current depending on the country — set it whichever way your market writes it.

You always have the last word. The correction above runs once, when you update. If you prefer the symbol in front for one of those five, untick the box and it stays the way you left it.

When a payment method is not ready

Admin → Payments checks each method and prints what is wrong next to it, in red, so you find out instead of your customer. Two of those checks are about currency, and both describe something that looks perfectly fine on your screen:

A gateway you installed as an add-on can add its own checks here too — a bank code it does not recognise, an account number that cannot be right — so read this column after setting one up. A method with nothing printed under its name has passed.

Letting a customer choose the currency for one payment

The display currency in a customer's profile changes everything they see — prices, orders, balance. That is more than someone needs when all they want is to pay this one top-up in their own money. So the Add Funds page offers the choice at the till: a row of currency buttons above the amount, and the minimum, the fee summary and the button all follow the one they press. Their wallet is still credited the same value in your base currency, and their profile is untouched.

Two things have to be true for a currency to appear there, and both are yours to arrange:

  1. The gateway states which currencies it settles. The regional processors do — Korapay, Paystack, Flutterwave, PhonePe and their kind — and their setup form has a Currencies field where you enter the ones your merchant account was approved for, comma separated (for example NGN,GHS,KES,ZAR). Card processors such as Stripe and PayPal, and offline methods such as a bank transfer, do not declare a list; those keep charging the customer's own display currency, exactly as before.
  2. You activated the currency in Admin → Currencies, with a rate. Without a rate there is nothing to convert the wallet credit from, so a currency your gateway supports but you have not activated is simply not offered.

If only one currency passes both tests there is nothing to choose, so no buttons are drawn. And when the customer's own currency is not one the gateway can take, the form is priced in the currency the gateway will really charge — the amount label names it, so what they read is what they are billed.

Your resellers get the same screen when they top up their balance with you.

8.5 Approving offline deposits

A card gateway confirms its own payments. A bank transfer, a mobile-money payment or any other offline method cannot: someone has to check the money arrived and release the balance. Those deposits wait in Admin → Finance → Deposits.

Each row shows the client, the method, the amount, the reference and — when the client sent one — a link to the proof of payment they attached. Approve credits their balance immediately, applying any matching top-up bonus. Reject marks the deposit as failed and credits nothing. An already approved deposit cannot be reversed from this screen; that is a wallet adjustment.

The menu entry carries a badge with the number still waiting, and the badge drops as soon as you settle one. Deposits taken by a reseller through their gateway are approved by the reseller, in their own panel — see §9.9.

The client is told either way Approving or rejecting sends the client a notification (§8.6) with the amount, so an offline payment is no longer a silent wait. The message never names who approved it, which is what keeps a reseller's storefront white-label.

8.6 Notifications

Every account — yours, your staff's, your resellers' and your clients' — has a bell in the top bar. The badge counts what is unread; opening the bell previews the latest, and clicking one takes you to the screen it is about and marks it read. View all notifications opens the full history, where you can filter by unread, mark items read one at a time or all at once, and delete them.

What arrives there:

Deposits
A deposit started through an offline method, and again when the client attaches the receipt — sent to whoever settles that panel's money. The client is told when it is approved or declined.
Subscriptions
Trial ending, renewal charged, payment failed, subscription suspended — sent to the reseller.
Balance
A reseller whose prepaid balance ran out, so their customers' orders are failing.
CRM
A channel token about to expire, expired, or rejected by the network.
Account
The welcome message a new account receives.

These do not replace the emails: the ones that were emailed before are still emailed, and now also appear in the bell. Email templates stay editable in Settings → Email templates.

Language A notification stores what it means, not a finished sentence, and the wording is built when it is read. So an alert raised by a background job at 3 am still arrives in whatever language the person reading it has chosen.
Turning off the auto-refresh The bell re-checks every 60 seconds. On very constrained shared hosting you can switch that off by adding RF_NOTIFICATIONS_POLL=0 to your .env (or set any other number of seconds), then clearing the cache with php artisan optimize:clear. With polling off the badge simply updates as you move between pages.

9. Resellers (child panels)

A reseller gets a panel of their own: their own address, their own name and colours, their own clients, their own catalogue prices, their own CRM inbox. To their customers it is a product, not a page of yours. Technically it is a child panel inside your installation, and the isolation is enforced at the database level — a reseller cannot read your data or another reseller's.

This chapter covers the reseller as a panel. Charging them a monthly fee for it is chapter 10.

9.1 The price chain

Four prices stack, and each participant only ever sees the two that concern them:

Provider cost        what you pay your upstream provider
  + your markup
= Owner price        your sale price
  + plan catalogue markup   (the cut attached to the reseller's plan, see 10.2)
= Reseller cost      what the reseller pays you per order
  + reseller margin
= Client price       what the reseller's own client pays

When a client of a reseller places an order, three things happen in one transaction:

Two ways to earn from a reseller You can take your cut on every order, by putting a catalogue markup on their plan; or you can give them your own price and charge a flat monthly fee instead. The seeded plans show both models: Free costs nothing but sells the catalogue at +30%, while Pro costs a monthly fee and sells at your own price. Mixing both is possible, but pick one story for each tier so a reseller can do the arithmetic.

9.2 Creating a child panel

Admin → Resellers → Resellers lists every child panel with its plan, subscription state, wallet balance and domains. Create reseller provisions everything in one action — the panel, its administrator account with the reseller role, and optionally their first subscription:

Panel name
The reseller's brand, used across their panel and e-mail.
Subdomain
The name under your platform host: max becomes max.panels.example.com. Requires RF_PANEL_BASE_HOST — without it the admin refuses to assign a subdomain and tells you which variable to set (§4.5).
Administrator
Name, e-mail and initial password of the person who will run the panel.
Primary colour
Their accent colour. They can change it later in their own settings.
Client template / landing template
Which storefront skin and which landing skin the panel opens with, chosen from the ones their plan includes.
Plan and trial
The subscription to start them on, with or without its trial period (§10).

From the same screen you can later adjust a reseller's balance, register a payment they made you, change their plan, edit their branding, or deactivate the panel.

9.3 Domains and SSL

A child panel can be served in two ways, and the reseller sees both on their own Domains screen:

RouteHow it worksRequires
Platform subdomain You give them name.panels.example.com. Nothing for them to configure; it works the moment you assign it. RF_PANEL_BASE_HOST, a wildcard DNS record and a wildcard certificate on your side.
Their own domain They add the domain in their panel and point its DNS at your server — an A record to RF_PANEL_IP for an apex domain, or a CNAME to RF_PANEL_CNAME_TARGET for a sub-domain. The panel gives them a TXT record to publish, checks it, and activates the domain once it verifies. At least one of the two DNS targets set in .env, and their plan allowing a custom domain.

Requests are routed by the Host header: the panel resolves which child panel owns the incoming host and serves that panel's branding, catalogue and prices.

Certificates are your hosting's job RedFamosa never issues TLS certificates. Each domain you serve needs one, from cPanel AutoSSL, from your VPS certificate tooling, or through a proxy such as a CDN that terminates TLS in front of you. A reseller's domain without a certificate loads with a browser warning, which is the fastest way to lose their customers — agree who handles it before you sell the tier.

9.4 Margin management

The reseller sets their own selling prices from Margins & Pricing in their panel — if their plan includes that module (§10.2). They can set a default margin over their cost for everything, override it for a whole category, and override it again for a single service. The most specific rule wins: service, then category, then the default.

Their clients never see your price, the provider's cost or anything else from upstream. They see one price, the reseller's.

Commission on own-supply services

A reseller on a plan with own providers (§9.7) sells two kinds of services: ones drawn from your catalogue, and ones imported from their own provider. The base commission applies to both. When the reseller saves a percentage commission — from Margins & Pricing, from My Services or from the welcome prompt on their first login — the panel recalculates the sale price of every service from their own provider and stores the result. From that point on their own catalogue reflects the same margin as the platform catalogue.

Two things are deliberately left alone:

Only percentage commissions propagate to stored prices A fixed-amount commission is added at checkout over the platform price and never touches a stored field. Own-supply services are priced at their stored sale price (the same way the platform prices its own catalogue), so a fixed commission has no equivalent to write to them. If the reseller uses a fixed commission and also runs their own provider, they should price their own services directly in My Services.

Price rounding

The Price rounding card at the bottom of Margins & Pricing lets the reseller choose how many decimal places are applied when their provider catalogue is synced or re-priced. The Decimal places selector accepts 1–4. Choosing 4 keeps the most precise result (e.g. 1.5000); choosing 1 rounds more aggressively (e.g. 1.5). Once chosen, click Save. When no choice has been made, the panel inherits the platform-wide setting configured in Admin → Settings → Currency.

Why rounding precision matters on cheap services A per-unit cost of 0.004 at a +30 % markup is 0.0052. Rounded to 2 decimal places that becomes 0.01 — a +150 % margin the reseller did not set. At 4 decimal places the stored price is 0.0052 and the margin is what was asked for. The default shipped with the panel is 4 for this reason.

9.5 Reseller balance

A reseller pays you the same way a client does: they top up their wallet through the payment methods you have enabled, and every order their clients place draws from it in real time. You can also register a payment manually from the reseller's row when they pay you by transfer.

An empty reseller wallet stops their shop If the reseller's balance cannot cover an order, the order fails at the debit and their client sees a plain "insufficient funds" message — with no hint that the reason is upstream. It looks like their panel is broken, and they will hear about it. Tell your resellers how the wallet works on day one, and keep an eye on the balance column in the reseller list.

9.6 Their own payment gateways

A reseller can collect from their own customers with their own gateway accounts, so the money arrives directly in their Stripe, PayPal or bank account and never passes through yours. They set it up in their panel under Finance → My Payment Methods, with the same form you use in Payments: driver, display name, minimum and maximum, fees, and the gateway credentials.

Each panel only ever sees its own methods. Yours are invisible to every reseller, and one reseller can never see or edit another's. Credentials are stored encrypted, exactly like yours.

The webhook address must be the reseller's own domain Gateways confirm a payment by calling the panel back, and the address they call is what identifies whose payment it is. The screen therefore shows a Webhook URL built on the reseller's own verified domain — they must paste that one into their gateway dashboard, not yours. A reseller who has not verified a domain yet (§9.3) is told to do that first instead of being given an address that would confirm payments against the wrong panel.

Only the reseller account owner reaches this screen: it cannot be delegated to a team seat, the same as every other control that moves money. Bank-transfer methods can carry an instructions image (a QR code or account details) shown to their customer at checkout, just like yours.

This is not the same as Add Funds in the reseller panel, which is how the reseller pays you through the gateways you enabled for them (§9.5). Two different directions of money, two different accounts.

9.7 Reseller-only mode

If your business is selling panels rather than services, switch on Reseller-only mode (no end customers on this platform) in Settings → Default setting. Your own client area then closes completely: the signup page is off, and existing client accounts can no longer sign in — they are told the platform has no client accounts, and a session that was already open is ended on the next page they open.

Your resellers are not affected in any way: their storefronts keep accepting signups and their customers keep signing in and ordering as before. Your staff and your reseller account owners sign in as usual, since neither is a customer of the platform.

Your public pages follow the mode on their own. The buttons that invited a visitor to open an account — on the landing page, and on the public service list if you publish one — stop offering one, because there is no account for them to open. If you also have Let visitors sign up as resellers on (§9.8) they point at that page instead, which is the one road that leads somewhere. With both switched off, the service list simply shows your prices and asks for nothing.

Nothing is deleted The mode closes a door, it does not remove data. Client accounts, their balances and their order history stay exactly as they are, and switching the mode back off restores the area unchanged. It is safe to try.

9.8 Self-serve reseller signup

By default a reseller only exists if you create one (§9.2). Turn on Let visitors sign up as resellers in Settings → Default setting and visitors can buy a panel themselves from a public page: they choose one of your public plans, pick their subdomain, create their account, and land inside their own panel with your catalogue already in it.

The price buttons on your landing page point at that page automatically, with the plan the visitor clicked already selected. Only plans marked public and active in §10.1 are offered, and a plan that is neither cannot be requested by hand either.

The page is off until you switch it on, and it is never shown on a reseller's own storefront: a reseller sells services, not memberships of your platform.

Your existing customers get their own door. While the signup is open, every logged-in customer sees a Get Your Own Panel entry in their dashboard sidebar. It runs the same signup on their existing account — no second registration, no new email. The page opens with a short “How it works” and your live plans (the pricing and features exactly as you configured them under Admin → Plans); the customer picks one, names their panel and claims a subdomain. Unlike the public page, every public plan is on offer here — this customer already has a wallet, so a paid plan simply follows the funds-first rule: the review step shows the plan price next to their current balance, the first period is charged from the balance the moment the panel is created, and when the balance does not cover it the conversion is blocked with an “Add funds first” shortcut instead (a plan with trial days starts on its trial, as configured). Nothing is created until then: the review step first shows the panel name, the full web address, the starting plan and the money involved, and states plainly that the customer account becomes a reseller account managed from the Panel area — only the explicit “Yes, convert my account” creates the panel, with the current wallet balance and details carried over. A customer who would rather keep that account as a customer account gets a link on the same page to sign out and register a separate reseller account instead. A customer who already owns a panel is simply sent to it.

9.9 Approving their customers' transfers

A card gateway confirms itself, but a bank transfer or a mobile-money payment is confirmed by a person: someone has to check the money arrived and release the balance. When the gateway belongs to the reseller (§9.6), that person is the reseller — the money went to their account, so only they can see it landed.

They do it in their panel under Finance → Customer Deposits, which lists every deposit their clients started, filtered by status and searchable by client. Each row shows the amount, the method, the reference and a link to the proof file the client attached. Approve credits that client's balance immediately; Reject marks the deposit as failed and credits nothing. A deposit that has already been approved cannot be reversed from here — that is a wallet adjustment, not a click.

The screen only ever shows that panel's own deposits, and only the reseller account owner reaches it. Your own Deposits screen is unchanged and keeps showing yours.

If you already run resellers Before this version a transfer paid to a reseller had nobody able to approve it, so it stayed pending. After updating, ask your resellers to open this screen: anything waiting there can now be settled normally.

On the customer's side, a deposit that is still pending keeps an Attach proof button in their payment history, so the receipt can be sent after the transfer is actually made — and replaced while the deposit is still pending. Once it is approved or rejected, the proof is fixed.

When a reseller runs out of balance A reseller's customer pays the reseller, and the reseller pays you (§9.5). If the reseller's own balance is empty, their customers' orders cannot go through: the customer is told the store cannot take orders at that moment and nothing is charged, and the reseller is emailed at once — at most one message per hour — so they can top up. That email is editable in Settings → Email templates under Panel balance ran out.

9.10 The languages their storefront speaks

A language in RedFamosa is two things: a row in the list under Admin → Languages, and a translation file inside the installation. That file is shared by every panel, so installing and translating languages stays with you: if a reseller could edit it, their wording would replace yours and every other reseller's.

What each reseller does decide is which of your languages their own panel and their customers' storefront offer, and which one a first-time visitor lands on. They set it in their panel under Settings → Languages: they tick the ones they want and pick a default. A reseller selling in one market can offer just that market's language instead of your whole list.

SettingWhat it does
Languages you offerThe rows their language switcher shows. Leave everything unticked to offer all of yours — which is how every panel behaves until a reseller changes it.
Default languageWhat a visitor who has never picked a language sees. Blank follows the default you set in Admin → Languages.

You keep control of the pool: switch a language off in Admin → Languages and it disappears from every storefront at once, whether or not a reseller had ticked it. A panel left with no language of its own falls back to offering all of yours, so a switcher is never empty.

The language follows the account When a signed-in user picks a language it is saved on their account, not just in that browser, so they get it again on their phone and after clearing cookies. A reseller narrowing their list does not strand anyone: a saved language that is no longer offered simply falls back to the panel default.

9.11 Selling below your own price

A serious reseller does not want your retail price — they want to be able to undercut you and still earn. That is what the catalogue price adjustment does when you give it a negative number.

Say a service costs you 1.00 from your provider and you sell it at 2.00 to your own customers. Put -10 on a plan and every reseller on that tier buys at 1.80. They can resell at 1.90 — under you — and still make 0.10 on every order, while you make 0.80 on the same order without touching it.

You setThe reseller pays youWho it is for
+302.60A free tier that costs you hosting and supply and pays you back per order.
02.00Your own price. You earn from their monthly plan fee instead.
-101.80A paid tier, or a reseller who brings volume and needs room to compete.
-1001.00Everything you make on that service handed over. The floor.
You can never sell below your own cost A discount stops at what you pay your provider for that service, and it is checked service by service — the same -40% can be comfortable on one service and below cost on the next, so the panel decides per row rather than per percentage. Nothing you can type on either screen makes you sell supply at a loss.

Where to set it. Two places, and the more specific one wins:

The reseller is never shown a "discount": the number they buy at simply is the price of the service on their screens and the amount their wallet is debited — one figure, from one calculation, so their price list and their invoices can never disagree. Their own margin, set in Margins & pricing, still stacks on top of it.

10. Reseller plans and subscriptions

Plans turn resellers into recurring revenue. A plan defines what a child panel can do and what it costs per period; the fee is taken automatically from the reseller's existing wallet, as a ledger entry, with no card checkout and no second payment gateway to configure.

How a reseller pays their plan A reseller keeps one prepaid wallet in your panel — the same wallet their clients' order costs come out of. A plan charge is a debit from that wallet, written as a plan_charge ledger entry. They keep it funded through Add funds exactly as before. Nothing new to install, and one balance to watch instead of two.

10.1 Creating plans

Admin → Resellers → Plans. Three plans are seeded so you have a working commercial structure on day one — Free, Pro and Business — and you can edit, rename or replace them entirely.

Name and slug
The slug is the stable internal key; leave it blank and it is derived from the name.
Price and currency
0 makes a free tier. The currency must be your base currency, because the charge is a wallet debit.
Billing period
monthly, quarterly, semiannual, annual or lifetime. A lifetime plan is charged once and never renews.
Trial days
Delays the first charge until the trial ends. 0 means no trial.
Grace days
How long a subscription stays past due before it is suspended. Three days by default.
Public
Public plans appear on the reseller's own upgrade page, where they can switch to them without asking you.
Highlights
The selling lines shown on the plan card. Free text — a highlight describes what the tier gives, it never enforces anything.

10.2 The feature matrix

A plan does not meter usage. There are no quotas on clients, services or orders per day — a reseller on the smallest tier can grow without hitting a wall, which is the point. What a plan decides is which modules their panel has, what they pay for the catalogue and how many domains they may bring.

Modules that are always included

Six modules can never be switched off, because a panel without them is not a panel: the dashboard, categories and services, orders, clients, the finance area (wallet, top-ups, ledger and plan billing) and settings with branding.

Optional modules

Twelve more are checkboxes on the plan form. All of them are on by default — you switch a module off to build a cheaper tier, never on. A plan created before a module existed still grants it.

ModuleWhat the reseller loses without it
Margins & pricingThe screen where they set their own markup over your catalogue.
CRM inbox & channelsThe whole omnichannel inbox, contacts and labels.
CRM AI agentsAutomatic AI replies on their channels.
CRM automationsRule-based flows on their conversations.
Support ticketsTheir clients can no longer open tickets.
Team seatsSub-accounts with their own permissions.
Content & blogNews, FAQs, blog posts and static pages.
Landing page studioTheir own editable landing page.
Custom domainThe domains screen. It stays visible even on a subdomain-only tier, so they can see what a higher tier would add.
Client portalThe storefront their clients buy from.
Reseller APIThe public API surface for their own clients.
BlacklistBlocking links, domains and words on their panel.

The commercial dials

SettingMeaningSeeded values
Catalogue price adjustment % What this tier pays for your catalogue, as a percentage of your own sale price. 0 means they buy at your price. A positive number is your cut on every order they place. A negative number is a wholesale discount, so the tier can sell under you and still earn — see §9.11. Free +30%, Pro 0%, Business 0%
Custom domains How many domains of their own they may connect. 0 means the platform subdomain only; -1 means unlimited. Free 0, Pro 1, Business 5
Own providers Whether they may connect their own upstream SMM provider and sell its catalogue alongside yours (§10.9). Off unless you turn it on. Business only
Client and landing skins Which storefront and landing designs the tier includes. A skin added in a future release is granted to existing plans rather than silently withheld. All, by default
Payment gateways Which gateways a reseller on this tier may connect to charge their own customers. The list is the gateways this installation has, add-ons included, so one you install later appears here by itself. Same rule as the skins: a gateway a stored plan never heard of is included until you untick it. All, by default
Untick a gateway and nothing breaks for anyone A reseller who connected that gateway before you unticked it keeps it working: it stays on their My Payment Methods screen marked as outside their plan, and they can switch it off or delete it. What they cannot do is add another one, or switch that one back on. Their customers with a payment in flight are unaffected. This is deliberately not a hidden row: a gateway that disappeared from their screen would go on charging their customers with nobody able to stop it.
Not the same as "how this reseller pays you" The gateway checkboxes on a plan decide what the reseller offers their customers. The Top-up methods checkboxes in Resellers → Edit decide which of your gateways they may use to fund their own wallet. Two different screens, two different pockets.
Plan terms are read live Features resolve through the plan row as it is now. Move Free from +30% to +40% and every Free reseller moves with it on their next order — there is no per-subscriber snapshot to update, and no way to grandfather one reseller except by putting them on another plan. Only the money already agreed is frozen: price, currency and billing period are copied onto the subscription when it starts, so a price rise never surprises an existing subscriber mid-term.

10.3 Managing resellers

The reseller list shows plan, subscription status, wallet balance and domain count for every child panel. Provisioning a new one is covered in §9.2; opening a row gives you the subscription controls below.

10.4 Subscription actions

Every action that moves money is permission-gated: comp and refund need their own permissions, which staff do not have unless you grant them.

ActionEffect
Assign or change planStarts a new subscription; the previous one is retired to history. Optional price override and trial.
CompA free subscription: active, priced at zero, never charged. For partners and for making an apology stick.
Override priceFuture periods bill your figure instead of the plan price. Recorded on the subscription.
Extend expiryPushes the period end and the next charge forward by N days at no cost — goodwill without touching the price.
Suspend / reactivateLocks or unlocks the reseller area and blocks new orders on their storefront.
Cancel now / at period endImmediate lock, or let them ride out the period they paid for and expire at the end of it.
Refund a chargeCredits a plan charge back to the reseller's wallet as a plan_refund. Idempotent: refunding twice does not pay twice.

10.5 Billing lifecycle

A background pass charges each due subscription exactly once per period. The charge is idempotent and transactional: the period's charge row is unique, the subscription is locked for the attempt, and a period that is already paid short-circuits before any debit. A subscription moves through six states:

trialing  --(charge succeeds at trial end)--> active
active    --(renewal succeeds)-------------> active
          --(not enough balance)-----------> past_due --(top up, charge ok)--> active
past_due  --(grace elapsed)----------------> suspended --(pay, reactivate)---> active
suspended --(retention elapsed, §10.6)------> panel permanently deleted
                                             cancelled / expired  (end of life)
A failed charge never advances the period A reseller who cannot pay stays on the same period and is retried. They never get a free month out of a failed charge, and they never get double-charged for catching up.

10.6 Dunning and suspension

When a renewal fails for lack of balance the subscription becomes past due and a grace deadline is set from the plan's grace days. The reseller is e-mailed on the first and third failed attempt — enough to be noticed, not enough to be noise. If the grace window runs out the subscription is suspended and a final notice goes out. A trial that is about to convert triggers its own heads-up a few days before the first charge.

What a suspended reseller sees Their storefront stops accepting new orders with a polite "temporarily unavailable" message, while orders already in flight keep running to completion — their customers are not punished for their unpaid invoice. Their own admin area is locked down to the billing and top-up pages, so they can always pay their way out without contacting you.

The retention window: suspended panels do not live forever

A panel suspended for non-payment is kept for a retention period — 30 days as shipped, counted from the day of the suspension — and then permanently deleted by the same billing pass: the panel, its customers, orders, domains and settings are removed for good. The reseller is warned by e-mail and in-app notification at 14, 7, 3 and 1 days before the deletion date (each warning sent once; both messages are editable under Settings → Email templates), and paying or being reactivated at any point cancels the countdown entirely. After a deletion the reseller's own login and wallet balance survive — their account lives on your main site — so they can top up and open a fresh panel whenever they are ready, and they receive a final notice saying exactly that.

10.7 Charge cadence

Billing rides on the same machinery as everything else in §13, so it works with or without a real cron:

Hourly is enough. A period boundary crossed within the hour is charged on the next pass, and a balance only changes when the reseller tops up.

10.8 Reseller self-serve billing

Resellers open Finance → Plan & Billing in their own panel to see their current plan, the next charge and their balance. When something is owed they get the amount due, a Pay now button that collects the period immediately from their wallet, and a link to add funds. They can also upgrade or downgrade to any public plan themselves — the difference is taken from the wallet, with no checkout and no ticket to you.

Everyone starts free, and pays before moving up

A visitor who signs up through /reseller-signup always lands on your free plan. Your paid tiers are shown on that page as what the panel can grow into, but they cannot be picked there: the ladder is sign up free, add funds, move up.

No free plan? Sign-up keeps working If your plan list has no public plan priced 0, the sign-up page offers every public plan exactly as it would otherwise, and Plans shows you a notice explaining why. A misconfiguration never turns a visitor away — and it never gives anything away either: a paid, no-trial plan contracted this way creates the panel locked until its first period is actually paid from the Billing screen, so nobody can ride the grace window for free.

10.9 Own SMM provider

On a plan with own providers enabled, the reseller gets Storefront → My Providers. There they register their own upstream provider — URL and API key, stored encrypted — test the connection, and import its catalogue into their own services, alongside the catalogue they resell from you.

Price the tier accordingly A reseller with their own provider stops buying from you on those services. That is exactly why the feature belongs on your most expensive tier: the subscription replaces the per-order margin you no longer earn. The seeded Business plan is built on that trade.

10.10 Plan limits reference

SettingEnforced where
Optional module on/offThe reseller's sidebar and the routes behind it — a module that is off has no menu entry and no reachable page.
Catalogue markup %Order creation on the reseller's storefront: it is added to the owner price to form the reseller's cost.
Custom domainsReseller → Domains, when they try to add one.
Own providersReseller → My Providers: the screen only exists on a tier that allows it.
Client and landing skinsThe template pickers, in the admin and in the reseller's own settings.
Payment gatewaysReseller → My Payment Methods: the driver picker, saving, and switching a gateway back on.
Subscription usableThe reseller area as a whole, and the storefront order block when they are suspended.
No subscription yet? Nothing is enforced A reseller you created without assigning a plan keeps full access until one is assigned. Opt-in features such as own providers stay off until a plan unlocks them.

11. Public API

RedFamosa exposes the standard SMM API, the same key + action contract your own providers speak. Any bot, reseller integration or shop plugin written for that format works against your panel without a single change — which is exactly why your customers will ask for it.

11.1 The endpoint

POST {APP_URL}/api/v2

{APP_URL}/api/v1 is an alias of the same handler, so clients configured for either version work. Requests may be sent as application/x-www-form-urlencoded, as JSON, or even as a query string on a GET — the parameters are read the same way. Two are always required:

key
The caller's API key. It identifies the user and the panel, so a reseller's client automatically gets the reseller's catalogue and prices.
action
The operation to perform.

Responses are JSON. Errors come back as {"error": "..."} with a human-readable message.

11.2 Actions

ActionParametersReturns
services— An array of the active services this key may order, each with service, name, type, category, rate, min, max, dripfeed, refill and cancel. rate is the price this caller pays.
add service, link, quantity; plus runs and interval for drip-feed, comments for custom comments, and the equivalent extras for the other service types {"order": 12345}
statusorder {"charge","start_count","status","remains","currency"}. A drip-feed parent answers with its status, its runs and the list of child order IDs.
status (multiple)orders — comma-separated IDs An object keyed by order ID; an unknown ID returns {"error": "..."} in its own slot instead of failing the whole call.
balance—{"balance","currency"}
refillorder, or orders for several{"refill": id}, or one entry per order.
refill_statusrefill, or refillsThe state of a refill request.
cancelorders — comma-separated IDsOne entry per order with its cancellation ID or an error.

A complete example

curl -X POST https://panel.example.com/api/v2 \
  -d "key=YOUR_API_KEY" \
  -d "action=add" \
  -d "service=143" \
  -d "link=https://example.com/profile/one" \
  -d "quantity=1000"

{"order":12345}

11.3 Idempotency-Key

Network calls fail halfway. A client that retries an add without protection buys the same order twice — and then asks you for a refund. Send a unique key with each order attempt and the problem disappears:

POST /api/v2
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

key=YOUR_API_KEY&action=add&service=143&link=https://example.com/p/1&quantity=1000

The first request creates the order and its answer is stored against the key. A later request with the same key replays that stored answer instead of creating a second order, for as long as RF_API_IDEMPOTENCY_TTL allows — 1,440 minutes, that is 24 hours, as shipped. If a retry arrives while the first one is still being processed, it is told so rather than being let through.

Generate one key per intent The key must identify the order the customer meant to place, not the HTTP call: generate a UUID when the customer presses the button and reuse it for every retry of that same order. Applies to add only — the other actions are already safe to repeat.

11.4 Rate limits

The API is limited per key: RF_API_RATE_LIMIT requests per minute, 120 as shipped, and 0 removes the limit entirely (§4.7). Over the limit the answer is HTTP 429 with a Retry-After header telling the client how many seconds to wait. Requests carrying an invalid key are throttled separately and more tightly, which is what stops someone guessing keys.

11.5 Outgoing webhooks

Instead of having integrations poll status in a loop, let the panel push. Configure the endpoint in Admin → Webhooks: the URL to call and the signing secret it will be signed with.

When an order changes state the panel POSTs JSON:

{
  "event":     "order.status_changed",
  "timestamp": "2026-01-31T10:24:05+00:00",
  "data": {
    "order":       12345,
    "status":      "Completed",
    "charge":      "4.5000",
    "start_count": 1200,
    "remains":     0,
    "currency":    "USD",
    "service":     143,
    "link":        "https://example.com/profile/one"
  }
}

Every delivery carries an X-Webhook-Signature header: an HMAC-SHA256 of the body computed with your signing secret. Verify it before you trust a payload — otherwise anyone who learns your endpoint can invent order updates.

Attempts
RF_WEBHOOK_MAX_ATTEMPTS, five as shipped.
Backoff
1 minute, 5 minutes, 15 minutes, then 1 hour — roughly an hour and a half of retries in total.
Timeout
RF_WEBHOOK_TIMEOUT seconds per attempt, ten as shipped. Answer 200 fast and do your work afterwards.
Webhooks are configured by the panel owner This is an operator-level integration, not a per-client setting: clients get their orders through the API, and you get the push notifications. There is no webhook field in the client area.

11.6 Getting the API key

A client finds their key in their own area under API, where they can issue one and regenerate it. The key is displayed once, at the moment it is created: only a hash of it is stored afterwards, so nobody — including you — can read it back later. A client who loses their key regenerates it, which invalidates the old one.

Each key belongs to one user in one panel. A reseller's client calling the API gets the reseller's catalogue at the reseller's prices, never yours. The API as a whole can be switched off for the panel in Settings → Default setting.

12. CRM — omnichannel

One inbox that turns WhatsApp, Messenger, Instagram, Telegram, e-mail, a web-chat widget and your own support tickets into a single conversation stream — answered by AI when you want, driven by automations, and wired to turn a chat into a paying client of the panel.

12.1 Overview

The CRM is built into the product. There is nothing extra to install: once the panel is installed it appears as the CRM group in the sidebar of the owner admin (/admin/crm) and of every reseller panel (/panel/crm).

What it gives you
  • A three-pane unified inbox
  • Seven channels, including internal tickets
  • AI agents with a knowledge base
  • A visual automation builder
  • The chat-to-client conversion funnel
  • An embeddable web-chat widget
  • Contacts, labels and quick replies
  • Reports, and full isolation per panel

In one paragraph: an operator — you, or one of your resellers — receives messages from every channel in one place. An AI agent can answer automatically using a knowledge base you paste in, and hand over to a human when it is out of its depth. An automation can run a whole flow: greet a prospect, create a client account on the panel, send the credentials over the same chat, force a password change on first sign-in and hand the conversation to a sales agent. That is the full path from a social-media message to a client who tops up and orders.

Roles at a glance

RoleWhat they do in the CRM
Owner / super adminEverything. Configures the Meta and Instagram apps, the AI keys and every setting, and sees the internal Support channel.
StaffWorks the inbox and manages agents, automations, contacts, labels and reports — but cannot touch channel credentials or CRM settings.
Reseller adminThe same CRM, scoped to their own panel: their own channels, their own AI keys, their own inbox. They never see yours.
ClientDoes not see the operator inbox at all. They chat through the web widget and open support tickets, which reach you as conversations.
Every panel is isolated, including in the background Each panel stores its own channel credentials, AI keys and settings, encrypted and invisible to any other panel. Each panel has its own webhook address and verify token (§12.14), so a reseller's WhatsApp messages route to the reseller and never to you. Inbox, contacts, automations, labels and reports are filtered to the current panel, and every queued job pins its panel before touching data.

12.2 Permissions and roles

The CRM adds eleven permissions to the panel's role system. They gate both the menu entries and the actions inside each screen.

PermissionGrants
crm.viewBaseline access to the CRM area.
crm.inboxOpen the inbox and read conversations.
crm.sendReply, write internal notes, assign and resolve.
crm.channels.manageSee the Channels screen and the token-health state.
crm.channels.credentialsWrite channel credentials: create, edit or delete a channel, connect through OAuth, sync WhatsApp templates, register the Telegram webhook.
crm.ai.manageCreate AI agents and manage the knowledge base.
crm.automations.manageBuild and switch on automations.
crm.contacts.manageManage the contact directory and quick replies.
crm.labels.manageManage labels and quick replies.
crm.reports.viewView the CRM reports.
crm.settings.manageOpen CRM Settings: the Meta and Instagram apps, verify token, AI keys, widget and operational toggles.

Who gets what out of the box

Rolecrm.channels.credentialscrm.settings.manageThe other nine
Super adminyesyesyes
Staffnonoyes
Reseller adminyesyesyes
Clientnonono
Staff cannot touch secrets, by design A staff member can work the entire inbox and even see the channel list, but can never overwrite an access token or app secret, delete a channel, or open CRM Settings. A reseller admin manages their own credentials, because they are theirs. If you want a staff member to connect channels, grant crm.channels.credentials to their role explicitly.

To build a chat-only agent role, create a role with just crm.view, crm.inbox and crm.send. Whether that agent sees every conversation in the panel or only their own is the agent visibility setting (§12.4).

12.3 The unified inbox

Open it at /admin/crm, or /panel/crm in a reseller panel. Three panes side by side:

PaneContents
Left — conversationsThe filtered list, most recent first, paginated (twenty-five per page as shipped). Each row shows the contact, the channel, a preview of the last message, how long ago it arrived and an unread badge.
Middle — threadThe full history, the channel banner (for example the state of the WhatsApp 24-hour window) and the reply box.
Right — contextThe contact card, the linked client account if there is one, labels, the assigned AI agent and human, and quick actions.

Tabs, filters and search

Status tabs
Open, Pending, Resolved, each with a live counter.
Assignment
All, mine, or unassigned.
Channel
Narrow to one channel type, or show everything.
Search
Free text across the conversations in the current tab.

The list refreshes by polling, and the open thread refreshes faster, so new messages appear without touching anything. There are no websockets — deliberately, because that is what makes the inbox work on plain shared hosting.

Working a conversation

Support tickets arrive here too Every panel has an implicit Support channel. When a client opens or replies to a support ticket — from their own area, unchanged for them — it appears in your inbox as an ordinary conversation. Reply from the inbox and it is written back to the ticket; resolve it here and the ticket closes. The ticket stays the system of record; the inbox is the single envelope your team works from. AI on support tickets is off by default.

12.4 CRM Settings — the one-time setup

Channels live at CRM → Channels, but before any Meta channel can be connected you must fill in your app details at CRM → CRM Settings. Secrets there are write-only — leaving a secret field blank keeps the stored value — and are encrypted at rest.

Meta app (WhatsApp, Messenger, Instagram messaging)

Meta App ID
From your app on the Meta developers site, under basic settings.
Meta App secret
Same page. Used server-side to verify webhook signatures and exchange OAuth codes.
Embedded Signup configuration ID
The WhatsApp Embedded Signup configuration from your Meta app. This is what enables the one-click Connect WhatsApp button.
Verify token
Any string you choose; leave it blank and one is generated. You paste the same value into your Meta app's webhook configuration.
Graph version
The Graph API version, v21.0 as shipped. Change it only when Meta asks you to.

Instagram app

Instagram messaging uses a separate child app with its own credentials — that is Meta's design, not ours. Enter its App ID and App secret here to enable the Connect Instagram button.

The two values you copy into Meta

Webhook URL
{APP_URL}/crm/webhooks/meta/{panelUid} — paste it into your Meta app as the callback URL. The identifier at the end belongs to this panel only.
OAuth redirect URI
{APP_URL}/crm/oauth/callback — add it to the valid OAuth redirect URIs. One redirect serves you and every reseller.

Paste your verify token alongside the webhook URL in the same Meta screen.

What to enable in the Meta app Add the products you need — WhatsApp, Messenger, Instagram — subscribe the app to webhooks for the messaging fields, and request the messaging permissions for each product. While your app is in development mode only users you added as testers or admins can connect it, which is the usual reason a connection works for you and not for a colleague.

The rest of the screen

AI keys
Panel-level OpenAI, Anthropic and Gemini keys, used when an agent has no key of its own (§12.11).
Knowledge base
Switch it on or off and set how many knowledge chunks are retrieved per answer (five by default).
Widget branding and allowed origins
The text under the widget and the list of sites allowed to embed it (§12.13).
Auto-resolve days
Automatically resolve conversations idle for N days. Zero disables it.
Agent visibility
All lets agents see the whole panel; assigned limits them to conversations assigned to them or unassigned.
Notification sound
Play a sound when an unread message arrives.
E-mail new-thread hours
How long before a reply by e-mail is treated as a new conversation rather than the same one.
AI on support
Whether the AI may answer internal support tickets. Off by default.
Welcome tour steps
The content used by the welcome-tour automation step.

12.5 WhatsApp (Cloud API)

The easy way. Once the App ID, App secret and Embedded Signup configuration ID are saved, a Connect WhatsApp button appears on the Channels screen.

  1. Click Connect WhatsApp. A Meta window opens.
  2. Sign in and choose — or create — the WhatsApp Business Account and the phone number to authorise.
  3. The window returns a code, which the panel exchanges server-side; your App secret never reaches the browser. The WhatsApp account is subscribed to your webhook and the channel is filled in automatically: phone number ID, business account ID and display number.

Manual alternative. If you already have the values from Meta's API setup screen, use Add channel → WhatsApp and fill in the phone number ID (the routing identifier), a permanent access token, the App secret, the WhatsApp Business Account ID and the display number.

The 24-hour window and templates WhatsApp only allows free-form messages within 24 hours of the customer's last message. Outside that window you must send an approved template. The inbox shows the window state on the thread banner and blocks a free-form send once it has closed. Use the channel menu's Sync templates to pull your approved templates from Meta.
A Meta channel needs its App secret Inbound webhook signatures are verified fail-closed: a WhatsApp, Messenger or Instagram channel with no stored App secret cannot verify incoming messages, so it will not receive any. Always save the secret.

12.6 Messenger

  1. On Channels, click Connect Messenger. Facebook's consent dialog opens, asking for the page-messaging permissions.
  2. Authorise your page. The panel exchanges the code server-side, subscribes the page to your webhook and creates the channel.

Manual alternative: Add channel → Messenger with the page ID (the routing identifier), a page access token and the App secret.

Outside the 24-hour window Messenger enforces its own 24-hour rule. Outside it, the panel sends with the human-agent message tag where the conversation is eligible for it.

12.7 Instagram

Enter the separate Instagram app credentials in CRM Settings first, then:

  1. Click Connect Instagram on the Channels screen.
  2. Authorise the Instagram business account — Instagram messaging requires a business or creator account linked to a Facebook page.
  3. The panel completes the token exchange, creates the channel and subscribes it. The long-lived token is stored and refreshed automatically before it expires.

Manual alternative: Add channel → Instagram with the Instagram account ID (the routing identifier), an access token and the App secret.

Token health The Channels screen shows a token badge. Instagram's long-lived tokens last around sixty days; a background task validates them and refreshes them ahead of the deadline. If one was revoked you will see a reconnect indicator — run the connect flow again and it is fixed.

12.8 Telegram

Telegram needs no Meta app, just a bot.

  1. In Telegram, ask BotFather for a new bot and copy the bot token and the bot's username.
  2. On Channels, Add channel → Telegram, paste both and save.
  3. Open the channel menu and click Register webhook. This tells Telegram to deliver updates to {APP_URL}/crm/webhooks/telegram/{channelUid}, with a secret token derived from your bot token so that only Telegram can post to it.

Messages to your bot now land in the inbox, and reply keyboards produced by button steps are rendered natively.

12.9 Email (IMAP and SMTP)

E-mail is pulled over IMAP — there is no webhook — and sent over SMTP. Use Add channel → Email:

Email address
The mailbox, which is also the routing identifier.
Password
The mailbox password. For a provider with two-factor authentication, an app password.
IMAP host and port
For incoming mail, typically port 993.
SMTP host and port
For outgoing replies, typically port 465 or 587.
From name
The display name on replies.

A background task polls the mailbox on an interval — every sixty seconds as shipped — so this channel depends on your automation mode actually running (§13). Replies are threaded by message ID, quoted text is trimmed, and automatic replies are filtered out instead of starting conversations with a robot.

12.10 Web Chat and Support

Add channel → Web Chat creates the widget channel:

Widget title
The header of the chat window.
Greeting
The first message a visitor sees.
Accent colour
The launcher and header colour. It starts from the panel's own primary colour, so a reseller's widget matches their brand without being told to.
Position
Bottom left or bottom right.

Saving gives you the script tag to paste into any site — see §12.13 for embedding, branding and security.

The Support channel, by contrast, is created for you: one per panel, not shown in the channel list and not editable. It exists only to bring your internal tickets into the inbox.

12.11 AI agents

Manage them at CRM → AI Agents. An agent is a reusable configuration that answers conversations automatically while the bot is active on them.

  1. New agent, and give it a name such as "Sales" or "FAQ bot".
  2. Pick a provider and a model.
  3. Optionally paste an API key for this agent; leave it blank to use the panel-level key. Test key validates it against the live provider before you save.
  4. Write the system prompt — the persona, the rules, the goal. This is where the quality of the whole thing is decided.
  5. Set the temperature and the maximum tokens per reply.
  6. Decide whether fallback to human is on, and save.
ProviderSelectable models
OpenAIgpt-4o-mini, gpt-4o, gpt-4.1-mini, gpt-4.1
Anthropicclaude-3-5-haiku-latest, claude-3-5-sonnet-latest, claude-sonnet-4-latest
Geminigemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash
Every panel pays for its own AI An agent uses its own key first and falls back to the panel-level key. Keys are never shared between panels, so each reseller pays for their own usage — you are not funding their bot. Knowledge-base embeddings are the one exception in kind: they always use an OpenAI key, the agent's or the panel's.

Knowledge base

While editing an agent, add knowledge sources: a title and the text — your FAQs, prices, policies, service descriptions. The panel splits it into chunks and indexes them; when the agent answers, the most relevant chunks are retrieved and injected into the prompt, so replies come from your content instead of from the model's imagination. How many chunks are used is the top K value in CRM Settings, five by default.

Handing over to a human

With fallback to human on, the agent is told it may emit a [TRANSFER] marker when it cannot help. When it does, the panel strips the marker from the text, pauses the bot on that conversation and assigns it to the least loaded member of the team. With the setting off, the agent never hands over by itself — a pure round-the-clock FAQ bot — though a human can always take over simply by replying.

Switching AI on

If the AI does not answer A conversation only gets an AI reply when the bot is active and not paused, the conversation has an agent (or the panel has a default one), there is a valid API key, and the incoming message is text — a bare image does not trigger a reply. For support tickets, AI on support must also be on. See §14.4.

12.12 Automations

Build flows at CRM → Automations. An automation is a trigger plus a flow of steps that you assemble from a palette.

Triggers

TriggerFires when
First messageA brand-new conversation starts. A channel-specific flow wins over an all-channels one.
KeywordAn incoming message contains the text you specify, matched without case sensitivity.
Every messageAny inbound message at all.
Conversation assignedA conversation is assigned to a team member.
Label addedA label is attached to a conversation.
Conversation resolvedA conversation is marked resolved — useful for a satisfaction follow-up.

The step palette

GroupSteps
Messaging Send message (interpolated text) · Send media (image, audio, video or document by URL, with a caption) · Send gallery · Send buttons (rendered natively where the channel supports it) · Ask question (waits for the reply and stores the answer for later steps)
Flow Condition · Branch (wait for a keyword reply and take the matching path) · Wait · Drip follow-up (a deferred message, optionally only if the contact has not replied)
Actions Add label · Remove label · Set status · Assign AI agent · Assign staff · Pause bot · Resume bot · Create client account · Call webhook (an outbound POST or GET with an interpolated body, protected against internal-address abuse and capped at ten seconds)

Placeholders

Text steps interpolate values from the conversation. The editor offers the common ones as clickable chips:

{{contact.name|there}}     the contact's name, or "there" when unknown
{{contact.email}}         {{contact.phone}}
{{company.name}}          the panel's name
{{agent.name}}            the assigned team member
{{answers.email}}         an answer captured by an "ask question" step

The engine also resolves {{channel.name}}, {{channel.type}}, {{company.email}} and {{message.content}}, and any {{answers.<key>}} you defined in an ask question step. The | syntax supplies the fallback when a value is empty.

Templates

Fourteen ready-made flows ship with the product, so you rarely start from an empty canvas: order funnels and catalogue welcomes for WhatsApp, an Instagram DM welcome, new-customer onboarding, the capture-and-convert funnel, a keyword pricing responder, social-proof conversion and nurture flows, a win-back sequence, web-chat lead capture, an FAQ triage, a Telegram start menu, a Messenger FAQ with hand-off, and a plain welcome. Load one and edit it — it is faster than designing a flow from scratch, and it shows you what the steps do.

The flagship flow: capture and convert

Start a new automation from the Capture & convert template. It builds a first-message flow that welcomes the prospect, creates a client account on your panel, sends a short tour and hands the conversation to a sales agent.

What the create client account step really does:

The password is forced to change Accounts created from a chat are flagged. On first sign-in the client is sent to a mandatory change-password screen before they can do anything else, and the account is marked activated once they have.
Accounts are never created behind your back The CRM does not create a client because someone said hello. A client account is only ever created by an explicit create client account step in an automation you built, so you decide the exact moment a prospect becomes a client.

Save the automation, then switch it on from the list. The runs counter shows how many times it has executed, and flows are validated server-side on save.

12.13 The web chat widget

The web-chat channel gives you a widget for any external site.

<script src="https://panel.example.com/crm/widget/CHANNEL_UID.js" async></script>

Paste it before </body>. The script injects a floating launcher with your title, greeting, colour and position. It has no dependencies, keeps its session in the visitor's browser storage and polls for new messages.

Title, greeting, colour and position come from the channel; the small branding line under the widget comes from CRM Settings — blank hides it, which is what a reseller wants for a white-label site.

Putting it on your own landing page

You do not need to edit a template. Open Settings → Analytics, paste the same snippet into the free-form field, and save. It is rendered in the <head> of your landing page and of every public page, so the launcher appears on your own site exactly as it does on an external one.

Turn the Analytics switch on That field is only rendered when the master Analytics enabled toggle on the same screen is on. Pasting the snippet with the toggle off saves it but prints nothing, and the widget never appears. The field is shared with Google Analytics, Tag Manager and Meta Pixel — the toggle governs all of them at once.

Copy the snippet from the channel's own card on the Channels screen: it is generated with your panel address. A snippet copied from another installation — a demo, or a second panel of your own — points at that installation, so the visitor's messages arrive in its inbox and never reach yours.

Security: allowed origins

The widget endpoints enforce a fail-closed list of allowed origins per panel. In CRM Settings → widget allowed origins, list the sites permitted to embed it, one per line:

Exact host
shop.example.com
Wildcard subdomain
.example.com — any sub-domain of it
Anything
* — not recommended in production

The panel's own domain is always allowed. An origin that is not on the list gets no CORS header at all, so the embed simply does not work — which is the point. The widget's session is an opaque server-side token rather than a guessable ID — it travels in an X-RF-Widget-Session header — and the endpoints are rate-limited.

Signed identity for signed-in visitors

If you embed the widget on a page where the visitor is already signed in to your panel, you can hand the widget a signed identity so their chats attach to their account and survive across sessions. Set a global before the script loads:

<script>
  window.RF_WIDGET_IDENTITY = {
    user_id: 4821,
    name:    "Alex Doe",
    email:   "alex@example.com",
    expires: 1750000000,          // unix timestamp
    sig:     "hmac-sha256-hex"    // see below
  };
</script>
<script src="https://panel.example.com/crm/widget/CHANNEL_UID.js" async></script>

Your server computes the signature as:

sig = HMAC-SHA256( "{channel_uid}|{user_id}|{expires}", APP_KEY )

The panel re-verifies the signature and the expiry on every call and confirms the user belongs to the channel's panel. An invalid or expired signature is treated as an anonymous visitor rather than an error, so a stale page never breaks the chat.

12.14 Webhooks per panel

Each panel has its own inbound endpoints. The Meta channels share one address per panel; Telegram has one per channel:

Meta (WhatsApp + Messenger + Instagram)
  {APP_URL}/crm/webhooks/meta/{panelUid}         GET and POST

Telegram
  {APP_URL}/crm/webhooks/telegram/{channelUid}   POST

Both values to copy are shown on each panel's own CRM Settings screen. E-mail uses no webhook, and the widget resolves its panel from the channel identifier in the script URL.

How the work behind an inbound message is executed — parsing, media download, AI reply, automations — depends on AUTOMATION_MODE, exactly as for the rest of the panel: the webhook answers immediately and hands the work to the background. See §13.

12.15 Security notes

13. Cron and automation

A panel is not a website that waits to be clicked. Orders have to be sent upstream, their status polled, drip-feed runs released, catalogues synced, mailboxes read, channel tokens refreshed and reseller subscriptions charged. All of that is background work, and AUTOMATION_MODE decides how it runs.

13.1 The three modes

ModeHow the work runsRight for
middleware Each incoming web request also drains a little background work, with a lock so two visitors never run the same pass twice and a hard limit on how much one request may do. Queued CRM work — incoming chats, mailbox fetches, AI replies — is processed the same way, and since a chat webhook is itself a web request, messages land in the inbox within seconds. No cron, no worker, nothing to configure. Shared hosting, and any panel with steady traffic. This is what the installer sets.
scheduler One cron entry, once a minute, drives everything — including a bounded pass over the queued CRM work. Work happens on time whether or not anyone is visiting. Any host where you can add a cron job. The best value for the least effort.
queue The same once-a-minute cron enqueues the work, and a persistent worker executes it immediately as jobs arrive. Highest throughput and the most responsive. A VPS with a process supervisor, and panels with real volume.
The honest limitation of middleware mode It only moves when someone visits your site. A panel with no traffic overnight dispatches nothing overnight, and a drip-feed run scheduled for 4 a.m. is released when the first visitor arrives in the morning. It is a perfectly good way to start, and the right answer on hosting without cron — but the day you sell drip-feed or promise fast delivery, move to scheduler.

You can switch mode without editing files, in Admin → Cron Jobs. That screen also lists each scheduled task with the time it last ran and a Run now button — the fastest way to prove that your background processing actually works.

13.2 Setting up cron on cPanel

In cPanel → Cron Jobs, add a single entry running every minute:

* * * * * /usr/local/bin/php /home/USERNAME/redfamosa/artisan schedule:run >> /dev/null 2>&1

Two things to get right:

Then set AUTOMATION_MODE=scheduler. That one cron line drives two scheduled tasks: the automation tick every minute, and reseller plan billing every hour.

13.3 Running a queue worker

On a VPS, set AUTOMATION_MODE=queue, keep the once-a-minute cron entry from §13.2, and run a persistent worker:

php artisan queue:work --sleep=3 --tries=3 --timeout=60 --max-time=3600
Queue mode still needs the cron line In this mode the scheduled tick does not do the work itself — it enqueues it, and the worker executes it. Without the cron nothing is ever enqueued and the panel goes quiet. Cron plus worker is the correct combination.

Use Supervisor or a systemd unit to keep the worker alive and restart it if it stops. --max-time=3600 makes it exit once an hour so the supervisor restarts it with fresh code and fresh memory — also the reason you must restart the worker after an update.

13.4 What runs in the background

How CRM messages are processed in each mode

Every CRM channel delivers through the job queue: a Messenger, Instagram, WhatsApp, Telegram or web-chat message arrives as a webhook, is verified, and is queued for processing; the same goes for mailbox fetches, AI replies and media downloads. Who runs that queue depends on the mode:

The inbox looks empty but senders swear they wrote? Open Admin → Cron Jobs and check the Process queued messages row: its last-run time tells you whether anything is draining the queue, and its Run now button processes the backlog immediately. In queue mode, also make sure your worker process is actually running.
One thing to check when something "stopped working" Open Admin → Cron Jobs and read the last-run column. Nine times out of ten, an order that never left, an e-mail that never arrived and a subscription that was never charged are the same problem: the background worker is not running.

14. Troubleshooting and FAQ

14.1 Installation

"Please provide a valid cache path"

The application cannot write its compiled views. The storage/framework folder is missing its sub-folders, or they are not writable. Make sure these exist and are writable by the user that runs PHP:

storage/framework/cache/data
storage/framework/sessions
storage/framework/views
storage/logs
bootstrap/cache

Some FTP clients and unzip tools silently drop empty directories, which is how the folders go missing in the first place. Create them by hand in File Manager, set them to 755, and reload.

"No application encryption key has been specified"

There is no .env file. RedFamosa creates it on the first request by copying .env.example and then generating the key — so this message means the copy could not happen. Check that .env.example is present in the project root (some extraction tools skip files whose names start with a dot) and that the project root is writable so the new file can be created. Copying .env.example to .env yourself and reloading the page fixes it immediately.

The wizard's buttons and links point at https:// but the site is on http://

Everything the panel generates — links, form actions, redirects — uses the scheme written in APP_URL. If that value starts with https while the site is still served over plain http, every link points at an address your server does not answer, and the panel looks broken even though nothing is wrong with it.

Fix it in one of two ways: install the certificate and really serve the site over HTTPS (recommended), or edit APP_URL in .env to start with http://. Then reload. If you already finished the installation, also make sure SESSION_SECURE_COOKIE matches: true on HTTPS, false on plain HTTP — a secure cookie on an insecure site means nobody can stay signed in.

A requirements check will not turn green

See the table in §3.3. The most common case on shared hosting is that the domain is running an older PHP version than the account's default: change it in MultiPHP Manager for that specific domain, not globally.

A 500 error, or a blank page, right after uploading

The panel loads, but nothing responds — buttons, menus and pop-ups do nothing

Every screen is painted correctly and then nothing works: menus will not open, dialogs never appear, no form saves, and no error message is shown anywhere. Open your browser's developer console (F12, tab Console) and you will find these two lines:

GET https://your-panel.com/livewire/livewire.min.js?id=… 404 (Not Found)
Uncaught ReferenceError: Alpine is not defined
From 1.9.0 the panel tells you this itself You no longer need to open the console to find any of it. When a staff screen finishes loading and its JavaScript engine is missing, the panel raises a dialog naming the exact address your server refused and the status it answered, prints the Nginx block below with a Copy button, and points you here. It appears on the admin and reseller consoles only — your clients are never shown it, because the fix is nothing they can act on — and a correctly configured panel never sees it at all.

That file is the panel's JavaScript engine, so a 404 on it leaves every screen looking right and completely dead. It is not a file missing from your upload: /livewire/livewire.min.js is an address answered by PHP, not a file on disk — which is why there is no livewire folder inside public/, and why creating one does not help.

What causes it is a rule in your web server that catches every address ending in .js and serves it from disk instead of passing it to PHP. Nginx templates shipped by server control panels — aaPanel among them — contain one by default. If your site's configuration has a block like this, that is the one:

location ~ .*\.(js|css)?$ { expires 12h; }

The fix, on Nginx. Add the block below to your site's configuration, placed above both location / and the .js rule. Do not delete or edit the .js rule itself — the rest of your static files should keep being served and cached exactly as they are:

location ^~ /livewire/ {
    try_files $uri $uri/ /index.php?$query_string;
}

The ^~ is the part that matters: it is the only prefix Nginx allows to win over a regular-expression rule such as the .js one. Save, reload Nginx, and then clear the compiled configuration:

php artisan optimize:clear
Behind Cloudflare or any CDN, the fix is only half done The 404 was served with a long cache lifetime, so your CDN kept a copy of it — and it keeps answering with that copy after the server is already fixed, which makes a correct fix look like a failed one. Purge the cache (in Cloudflare: Caching → Configuration → Purge Everything) and reload the panel with Ctrl+F5, so your own browser drops its copy as well.

How to check it without guessing. Copy the address from the console — including the ?id=… at the end — and open it in a browser tab on its own. A page full of JavaScript means it is fixed; a 404 page means the change has not taken effect yet. Test it with that ?id=…: what follows the question mark is part of the cached address, so the same path without it can answer perfectly while the panel keeps failing. Back in the panel, press Ctrl+F5 and confirm the console has no red lines left.

On Apache and LiteSpeed you should not need any of this — the .htaccess files included in the download already route these addresses correctly. If your host lets you switch the site's web server, that is also a valid one-click way out.

If you cannot edit the Nginx configuration at all, you can turn that address into a real file, which any configuration serves:

php artisan livewire:publish --assets

That writes the JavaScript into public/vendor/livewire/ and the panel loads it from there instead. One thing to remember: the published copy is a snapshot, so run the command again after every RedFamosa update — your browser console warns you when it has fallen behind.

A screen shows a “403 Forbidden” page inside a box, with the panel still visible around it

One screen — often Settings — stops saving, and instead of a message from the panel you get a framed box containing a 403 Forbidden page from your web server (LiteSpeed, Nginx or Apache), while the rest of the panel is painted normally behind it.

That box is not a RedFamosa screen. When a request comes back with a reply the panel cannot understand, it shows you the server's raw answer rather than hiding it. So what you are reading is your own server refusing the request before PHP ever ran — the panel never received it and has nothing to report.

The cause is almost always ModSecurity (or a similar web application firewall), which most control panels — cPanel, aaPanel, Imunify360, Plesk — enable by default. It inspects what is being submitted, and a value that merely looks like an attempt to reach a system file is enough. Text such as ../, .htaccess, .env or /etc/ inside a field is the usual trigger — a canonical URL, an image path or a meta description are the fields where that happens by accident. Because the value is submitted again on every action of that screen, the screen keeps failing until the value is removed or the rule is adjusted.

Confirm it in one minute. Open your browser's developer tools (F12), tab Network, and repeat the action. A request to /livewire/update with status 403 is your firewall; if instead the request succeeds and the panel shows its own red message, the problem is in the panel and the message tells you what it is.

Getting it fixed. Two things, in this order:

  1. Reload the screen first. If it works again, the value was never saved — whatever you had just typed is the trigger. Re-enter it without the path-like part (a full address such as https://your-panel.com/logo.png rather than ../uploads/logo.png).
  2. If it keeps failing, the value is stored and your host has to allow it. In your control panel open ModSecurity's log — cPanel: Security → ModSecurity Tools → Hits List; aaPanel: the firewall's log tab — and look at the most recent entry. It names the rule id and the exact text that matched. Give your host that rule id and ask them to disable it for your domain, or only for the address /livewire/update. That is a one-line change on their side, and nothing else about your site's protection changes.
Why we do not simply avoid those characters Your firewall's rules are yours to tune: the same rule that blocks a canonical URL here would block a customer writing ../ in a support ticket, or a service description that mentions a file path. Turning the rule off for one address keeps the protection everywhere else, which is why it is the fix we recommend rather than restricting what you are allowed to type.

The installer opens again on a panel that is already installed

storage/app/installed.lock is missing — almost always because storage/ was overwritten during an update. Do not run the wizard again: it would try to migrate an existing database. Restore the file from your backup, or create it with any content, and the panel comes straight back.

14.2 Orders and providers

A very large provider catalogue is slow, or runs out of memory

A provider catalogue is downloaded whole, so the memory your server allows PHP is what sets the practical ceiling. These are measured figures, on a catalogue whose services carry long descriptions — a catalogue without descriptions is roughly a third of the size:

Services at the providermemory_limit you need
up to ~6,000128M (the common shared-hosting default) is enough
~12,000256M
more than that512M, and expect the first load to take a few seconds

If the Manage catalogue screen shows a server error or a blank page on a big catalogue, raise memory_limit in your hosting control panel and reload. The symptom is the page failing rather than loading slowly, and it appears on every action of that screen — searching and filtering too, not just the import buttons.

Two settings on the database side are worth checking on a large catalogue as well, because the panel keeps the fetched catalogue in its cache: max_allowed_packet (4 MB on a lot of shared hosting — 64 MB is a comfortable value) and, if your .env has CACHE_STORE=database, the size of the cache table rows. From version 1.9.0 the panel stores the catalogue in small pieces and simply skips the cache when the store refuses it, so neither one can break the screen any more — at worst the catalogue is re-fetched from your provider more often, which is slower but works.

Orders sit in "Awaiting" and never leave

Nothing is dispatching them. Open Admin → Cron Jobs and look at the last run of the automation tick. In middleware mode, remember the work only happens when someone visits the site — open the storefront in another tab and watch the timestamp move. If it does not move at all, see §13.

Orders end up in "Error"

The provider rejected them, and its own message is stored on the order — open one and read it. The usual causes, in order of likelihood: your balance at the provider is exhausted; the provider changed or removed that service ID; the link does not match what the service expects; the quantity is outside the provider's own limits. Fix the cause, then retry the order or cancel it to refund the client (§7.5).

What does "Simulated" mean?

It is a manual marker meaning "this order never really went upstream". Nothing sets it automatically. It exists so an operator can record the truth about an order instead of quietly marking it complete — and note that setting it does not refund anything: to return the money you cancel the order.

Can I connect more than one provider?

Yes, as many as you like. Any provider speaking the standard SMM API works. Different services can come from different providers, which is also how you build redundancy: keep a second provider configured for your best-selling services and repoint the service if the first one degrades.

A drip-feed order is releasing its runs late

Expected in middleware mode, where runs are released by visitor traffic. Move to scheduler mode (§13.2).

14.3 Billing and resellers

A client's balance is wrong

Balances are the sum of ledger entries, so there is no field to correct. Open the user in Admin → Users and adjust the balance with a positive or negative amount and a reason: that writes a new ledger entry and the displayed balance updates at once, with the history intact.

A card payment succeeded but the wallet did not move

The gateway's notification is not reaching you. Check that the webhook endpoint is registered in the gateway dashboard with the right address ({APP_URL}/api/webhooks/stripe or {APP_URL}/api/webhooks/paypal), that the signing secret in the payment method matches the one the gateway shows, and that APP_URL is the address the gateway can actually reach.

A reseller's subdomain does not resolve

Three things must all be true: RF_PANEL_BASE_HOST is set in .env, a wildcard DNS record for that host points at your server, and your web server answers for the wildcard with the same document root as the main panel. See §4.5.

A reseller cannot add their own domain

Either their plan does not allow custom domains — check the plan's domain count (§10.2) — or you have not filled in RF_PANEL_IP or RF_PANEL_CNAME_TARGET, in which case they are being asked to point their DNS at nothing.

A reseller says their clients cannot order

Look at two columns in the reseller list: their wallet balance and their subscription status. An empty wallet fails the debit for every order their clients place, and a suspended subscription blocks new orders on their storefront on purpose (§10.6).

14.4 CRM and channels

The "Connect WhatsApp / Messenger / Instagram" button is not there

Those one-click buttons only appear once the panel's app credentials are saved. For WhatsApp and Messenger you need the Meta App ID and App secret — plus the Embedded Signup configuration ID for the WhatsApp one-click flow; Instagram needs its own separate App ID and secret. You can always create the channel manually instead (§12.5–§12.7).

WhatsApp messages will not send

You are almost certainly outside the 24-hour window: free-form messages are only allowed within 24 hours of the customer's last message, and after that you must send an approved template. Run Sync templates on the channel and send one. If that is not it, test the channel's access token from its menu.

The AI does not reply

Check in this order: is there a default agent, or an agent assigned to that conversation; is the bot active and not paused (a human reply pauses it); is there a valid API key on the agent or on the panel; for a support ticket, is AI on support switched on. And remember the agent answers text — a bare image does not trigger a reply.

Telegram messages never arrive

Open the channel's menu and click Register webhook. Until you do, Telegram does not know where to deliver anything.

E-mails never arrive in the inbox

E-mail is pulled on a schedule, so your automation mode has to be running (§13). Then check the IMAP host and port, and make sure you used an app password if the mailbox has two-factor authentication.

The web widget does not connect

Add the site's domain to the widget's allowed origins in CRM Settings (§12.13). Requests from a domain that is not on the list are refused. Also confirm the channel is active and that the script URL carries the right channel identifier.

Messages are landing in the wrong panel

Each panel must copy its own webhook URL and verify token, from its own CRM Settings, into its own Meta app. The panel is chosen by the identifier in the webhook path, so a reseller who reuses your URL receives your routing.

A staff member cannot connect a channel or open CRM Settings

That is intended: staff do not hold crm.channels.credentials or crm.settings.manage. Grant those to their role if you want them to, or do the connection yourself (§12.2).

An Instagram channel shows "reconnect"

Instagram's long-lived tokens expire after about sixty days. The panel refreshes them automatically, but a token that was revoked cannot be refreshed — run Connect Instagram again.

Where do clients created from a chat end up?

They become ordinary client accounts on the same panel as the conversation, with a wallet created on their first credit or order. They sign in at that panel's client area, are forced to change their password once, and behave like any other client from then on (§12.12).

14.5 Maintenance

I changed APP_URL and the old address is still used

Clear the compiled configuration: php artisan optimize:clear. Without SSH, deleting the files inside bootstrap/cache/ has the same effect — they are regenerated on the next request.

How do I change the panel's language, or add one?

Admin → Languages is where the list lives and where the default is set; individual users choose their own in their profile. English and Spanish ship enabled, with several more locales listed and switched off — including a complete Arabic translation. Languages marked as RTL (Arabic ships that way) mirror the whole interface automatically — landing page, client portal, admin and reseller panels.

How do I stop new registrations?

Settings → Default setting, and turn client sign-up off. The register link disappears and the route stops accepting registrations; you can still create clients yourself from the admin.

Where are the logs?

storage/logs/. The application log rotates daily and keeps a fortnight by default. If you are chasing a problem, set LOG_LEVEL=debug in .env, reproduce it, then put the value back to error — debug logging on a busy panel fills a disk faster than you would expect.

How do I update to a new version?

See §3.10. In short: back up, upload over the top without touching .env or storage/, run the migrations, clear the caches, and restart the queue worker if you use one.

15. Support and changelog

15.1 Getting support

If the manual has not answered your question:

What to include so the first reply is the useful one Your RedFamosa version, your PHP version, whether you installed with Production or Demo data, what you did, what you expected and what happened instead — and the last lines of storage/logs/laravel.log. A stack trace usually identifies the problem instantly. Never paste your .env, your APP_KEY or any live API key into a support message.

15.2 What is included

ModuleWhat it isDocumented in
SMM panelProviders, catalogue, orders, wallet and billing — the core business.§5–§8
Reseller panelsChild panels on their own domains, with their own clients and prices.§9
Plans & subscriptionsRecurring revenue from resellers, billed from their wallet.§10
Public APIThe standard SMM API for your clients' own tools.§11
CRM — omnichannelUnified inbox for seven channels, AI agents, automations and the web widget.§12
Support ticketsTicketing for your clients, bridged into the CRM inbox.§12.3
ContentBlog, static and legal pages, FAQs and news announcements — per panel, including per reseller.Admin → Blog / Pages / FAQs / News
Landing pageAn editable public landing page built from blocks you switch on, off and reorder, with eight designs: Nova, Horizon, Velocity, Momentum, Franchise, Boost, Neon and Pulse.Admin → Website → Landing Page
Client storefront skinsThree client-area designs: Aurora, Vibrant and Midnight, selectable per panel.Admin → Website → Client Portal
Social sign-inGoogle (including One Tap) and Facebook, configured per panel.Settings → Social login
BlacklistsBlock by IP, link or e-mail, panel-local or global across every panel.Admin → Blacklist
ReportsA year-at-a-glance calendar of payments, orders, tickets or profit.Admin → Reports

15.3 Changelog

Version 1.9.0

Version 1.8.0

Version 1.7.0

Version 1.6.1

Version 1.6.0

Version 1.5.1

Fixed: updating from 1.3.x no longer shows a brief server error at the moment the new files are applied — every screen involved in an update (the update screen, the panel layout and the “we are updating” page) can now be rendered safely by the previous version while the switch happens, and the “we are updating” page is fully self-contained so it can never fail when it is needed. Improved: while an update is applying its files, an admin who browses to another page sees the “we are updating” notice instead of a half-updated page (the System Update screen itself stays available), and the PHP opcode cache is reset automatically after every update — from the System Update screen and from the /update assistant alike — so aggressive host caches cannot keep serving the old version.

Version 1.5.0

New: an amber ticket badge in the admin menu shows how many tickets are waiting on staff (new tickets and customer replies); it updates live as tickets are answered or closed and the reseller panel gets the same badge for its own tickets. Email alerts for the operator: a new ticket, a new deposit (gateway payments and offline deposits awaiting approval) and every new customer sign-up now also send an email to the staff who handle them, so you stay updated without being logged in — all three messages are editable under Admin → Email Templates (“New ticket alert”, “New deposit alert”, “New sign-up alert”). Ticket categories manager: Admin → Tickets → Categories lets you define your own categories and subcategories; they replace the built-in list (AI Support, Fake Complete, Human Support) on the customer's new-ticket form. Rename, hide or delete them at any time — existing tickets always keep the category they were opened with, and with no custom categories the shipped list keeps working as before. Broadcast: Admin → Broadcast composes one message and emails it to every customer of your panel without publishing anything on the site (ideal for “new payment method”, “new services” or maintenance notices). Messages are sent individually so recipients never see each other's addresses, delivery runs in the background, and every past broadcast stays listed with its recipient count. DB: two new tables (ticket_categories, broadcasts); both migrations are additive and run automatically during the update.

Version 1.4.2

Fixed: updating from an older version (1.3.x and earlier) through the System Update screen no longer ends in a server error at the moment the new files are applied. Right after the swap, the still-running old version used to draw the new screen and crash on routes it never had; that screen now detects the situation, shows “New version applied — reloading” and reloads itself, and the new code finishes the database and cleanup steps on its own. If you ever see an error on that screen mid-update, simply reload the page — the update completes itself. Changing your profile photo now shows the new photo immediately on every screen: each upload is stored under a fresh filename, so browser and CDN caches can no longer keep serving the previous picture (admin, client and reseller profiles).

Version 1.4.1

Fixed: the in-app updater now survives hostings that kill the request which swaps the new files into place — the next progress tick recognises the files are already on the new version and carries on with the database and cleanup steps instead of stopping with “No staged update found”, and reloading the System Update screen finishes a half-applied update on its own even when the release carries no new database changes. The moment the files are applied no longer renders a reply from mixed old/new code, which could produce a server error on some hostings. Improved: after a successful update the applied ZIP is removed from “Available packages” automatically, so a stale Update button can no longer invite a second run of a package that is already installed.

Version 1.4.0

New: Automatic cleanup of unpaid panels, with fair warning — a reseller panel suspended for non-payment is kept for a retention period (30 days as shipped; RF_PURGE_SUSPENDED_AFTER_DAYS in .env, 0 disables it) and then permanently deleted by the billing cron. The reseller is warned by e-mail and in-app notification at 14/7/3/1 days before the date, renewing cancels the countdown, their own login and wallet balance always survive, and panels you suspended by hand are never touched (see §10.6). Paid plans in “Get Your Own Panel” — existing customers upgrading to a panel can now contract any public plan, funds-first: the review step shows the price next to their balance, the first period is charged from the wallet at creation, and an insufficient balance blocks the conversion with an “Add funds first” shortcut (see §9.8). Security: on installs with no free public plan, a paid no-trial panel created through the public signup is now born locked until its first period is paid — it can no longer operate through the grace window unpaid. “Get Your Own Panel” inside the customer dashboard — when the reseller signup is open (see §9.8), logged-in customers get a sidebar entry that launches their own white-label panel on their existing account: balance and details carry over, no second registration. The page shows your live plans and a review step — the panel is only created after the customer explicitly confirms converting their account, and a link offers registering a separate reseller account instead. Custom comments on the order form — services of the “custom comments” / “mentions with custom list” type now show a comment box on the New Order page (one comment per line; the receipt prices the order by the number of lines — previously these services could only be ordered through the API). An automatic notification system across the whole panel. Customers get an in-app notification (bell) and, where it matters, an email the moment something happens to them: a support reply (deep-linking straight into the ticket, whether staff answered from the ticket screen or the CRM inbox), an order confirmation and its outcome (completed / partial / canceled / refunded), and every credited deposit — any gateway or a manual approval. Operators are pinged too — and since v1.5.0 by email as well as bell: new tickets reach the staff who manage tickets, deposits reach the money managers, and new sign-ups reach the user managers. On a reseller panel all of this goes to the reseller, never the parent panel's staff. Broadcast announcements: publishing a News item now offers “Notify my customers” (with an optional “Also send it by email” box) — every customer of the panel gets a notification linking to the announcement; each announcement broadcasts only once and the fan-out runs in the background. Resellers have the same option for their own storefront News. Four new editable email templates back these messages (Admin → Email Templates → “Orders, Deposits & Support”: Order received, Deposit credited, Ticket reply, Announcement), each with placeholders, live preview and test send. Also new: full right-to-left (RTL) support — every screen (landing page, client portal, admin area, reseller panels and e-mails) mirrors automatically whenever the visitor's language is marked as RTL in Admin → Languages. An Arabic translation (100% of the interface, Modern Standard Arabic) is included out of the box and ships already marked as RTL — enable it in Admin → Languages. An optional WhatsApp number at sign-up: turn on Settings → Default setting → “Ask for a WhatsApp number at sign-up” (resellers have their own switch in Panel Settings) and the registration form gains an optional WhatsApp field; the number shows in Admin → Users and is editable there and in the client's Profile. The client portal's support chat now uses honest branding: it presents itself as “AI Support” only when an active AI agent is configured, and as plain “Support” (human team) otherwise; its red button is now labelled “Create New Ticket”. Improved: chat content in the CRM inbox (bubbles, previews, internal notes) lays out each message in its own language direction, so mixed-language conversations read naturally either way; URLs, phone numbers and e-mail fields keep their left-to-right order inside an RTL interface; 30 previously untranslatable interface strings (payment and integration form hints) were added to the language files. Fixed: installations made from an early package could serve stale uploaded images (profile pictures, blog covers, payment-method logos) — updating now repairs the storage link automatically and existing uploads are kept; service rates no longer show dead trailing zeros (a whole-number rate on a 0-decimals panel reads 18,200, not 18,200.0000, while sub-cent rates keep every meaningful decimal); New Order and Mass Order forms no longer overflow the screen on mobile when a service has a long name; one Binance manual payment instruction that appeared in Spanish on English installations is now properly translatable.

Version 1.3.12

Fixed: outgoing e-mail (SMTP) failed on every installation with “The ‘tls’ scheme is not supported” as soon as SMTP was configured in Admin → Settings → Email. The panel now derives the transport scheme from the encryption you pick (SSL → smtps, STARTTLS/none → smtp), so the settings saved in the admin are always the ones used to send. Updating fixes existing installations as-is — no .env changes needed.

Version 1.3.11

One major update carrying everything built since v1.0.2: wholesale pricing so resellers can undercut you and still earn, self-serve reseller signup, add-ons, one-click updates from the admin panel, a notification centre, per-panel languages, a live CRM inbox — and a long list of fixes across the reseller pricing chain, per-panel account isolation and the client experience.

Updating from v1.0.2: v1.0.2 predates the System Update screen this release introduces, so this one update is applied by uploading the files and finishing at yourpanel.com/update — see §3.10. Every later update can then be applied from Admin → System Update in one click.

Version 1.0.2

This release makes every switch, field and dropdown in Settings do what its label says. We reviewed all of them and reconnected each control that was not wired to anything.

Version 1.0.1

Version 1.0.0 — first public release

Under consideration for future releases

Not part of version 1.3.5 and not a commitment, but the directions the product is most likely to grow in:

16. Add-ons and payment gateways

16.1 What add-ons are

An add-on is a self-contained folder under addons/ that extends the panel without editing a single core file. It registers itself through hooks — the same hooks the core uses internally — so an add-on can contribute a payment gateway, its own routes, its own admin screens and its own settings, and can be switched off again without leaving anything behind.

Admin → Add-ons lists what is installed, with the version each one declares and a link to its settings. An add-on that is disabled stops registering its hooks immediately: its driver disappears from the selects, its routes stop resolving, and the data it wrote stays in the database untouched, so enabling it again picks up exactly where it left off.

Installing one. Add-ons are separate packages, each with its own version and its own ZIP: the panel ships with none installed and addons/ starts empty. Click Upload Add-on, choose the .zip you downloaded, and it is validated and installed — you switch it on afterwards with Enable. An add-on that brings database tables shows Upgrade Database when it has migrations left to run.

If the upload does not go through. Press Install and the screen answers “The package field is required” even though you picked the file? Then the file never reached the server, and the size of your package has nothing to do with it — a security filter in front of your site, a proxy such as Cloudflare, a session that expired while the dialog was open, or a storage/ folder your PHP user cannot write to will all stop it in the same silent way. You do not need to find out which: extract the ZIP on your computer and copy the folder inside it into addons/ with your FTP client or File Manager. Reload Admin → Add-ons and the package is listed as Found in the addons folder with an Install button next to it. The result is identical to uploading — same installer, same checks.

Updating an add-on An add-on carries its own version, independent of the panel's: upload the newer ZIP from the same screen and its files are replaced, its registry entry moves to the new version and — when the add-on is enabled — its new migrations run right there. A package older than the one you have is refused, so an update can never silently roll code back. Uninstalling removes the add-on from the panel without deleting the data it wrote.
What happens to your add-ons when you update the panel Add-ons live outside the core, so a panel update never replaces them. It does check on them afterwards, once, for each add-on you have enabled: if the add-on's files ask for a newer panel than the one you are now running it is switched off rather than left to break a page, and you are told which one; if it has migrations of its own that never ran, they run. Both routes do this — Admin → System Update and the assistant at /update — and both list what they did.
If you build add-ons yourself Two rules the panel enforces. Tables must carry your add-on's own name — the slug with hyphens as underscores, then an underscore: my_addon_invoices, never invoices. A package whose migrations claim a table outside that prefix is refused at upload, because a name that collides with the panel's own schema puts the operator's data at risk. And settings do not need a table at all: every add-on owns a JSON bag on its registry row, so a later version can add options without a migration. The full contract is in addons/README.md inside the package.

16.2 Payment gateways

A payment method in Admin → Payments is one gateway configured and named by you. You may create as many as you like, including several on the same driver — a live one and a sandbox one, or one per market — and each has its own credentials, its own minimum and maximum, its own sort order and its own on/off switch.

Three drivers are built into the panel; further gateways arrive as add-ons (§16.1), which is how the Korapay driver below is installed:

DriverWhat it acceptsConfirmation
Manual / Bank transferAnything you accept off-platform: bank transfer, cash, a local wallet. You write the instructions the client sees and may attach an image (QR, bank details).You approve or reject each deposit in Payments. Nothing is credited until you do.
StripeCards, and whatever else your Stripe account has enabled.Signed webhook, credited automatically.
PayPalPayPal balance and cards.Signed webhook, credited automatically.
Korapay (separate add-on)Cards, bank transfer, pay-with-bank and mobile money across Africa.Signed webhook, credited automatically.

Every automatic gateway needs its webhook registered on the provider's side, and every gateway modal shows its own webhook URL in a copyable field — copy it from there rather than typing it, so the path is always right for your domain.

Which methods a client sees. A gateway declares the currencies it can charge in. A client whose display currency is not one of them is not blocked: the panel converts the amount at your configured exchange rate (§8.4) and charges the gateway in a currency it does accept. Either way the wallet is credited in the base currency, and the invoice states what the client actually paid, in the currency they paid it in.

Adding a gateway that is not on the list A gateway is an add-on, not a core change. An add-on declares its driver key and label, the credential fields its modal should show, and its webhook route; the checkout, the ledger, the bonuses and the invoice already work for it. addons/README.md in your installation states the package layout and the manifest a developer has to produce; the Korapay add-on is a complete working example of exactly that, in three files.

16.3 Setting up Korapay

Korapay settles in NGN, KES, GHS, USD, XAF, XOF and ZAR, which makes it the practical way to take money across much of Africa. It is an add-on: install korapay-gateway.zip from Admin → Add-ons and press Enable before the steps below — until you do, the driver does not appear in the Payments screen.

What you need: a Korapay merchant account, and its Public Key and Secret Key (Korapay Dashboard → Settings → API Keys). Korapay issues a separate pair for test mode and for live mode — the panel uses whichever pair you paste.

  1. Open Admin → Payments and click Add payment method.
  2. Choose the Korapay driver and give the method the name your clients will see on the Add Funds page.
  3. Paste the Public Key and the Secret Key.
  4. In Currencies, list the codes this merchant account settles in, separated by commas — NGN on its own, or NGN,GHS,KES if your account is enabled for several. One method covers them all; you do not need one method per currency. Left empty it falls back to NGN, so the gateway never claims to accept everything.
  5. Copy the webhook URL shown in the modal and paste it into Korapay Dashboard → Settings → Webhooks. Without it, payments succeed at Korapay and never reach the wallet.
  6. Save, switch the method on, and send yourself one small real top-up before announcing it.

What a client experiences. They pick Korapay on Add Funds and enter an amount in their own currency. The panel opens Korapay's own hosted checkout — card details never touch your server — charging in the currency the merchant account settles in, converting first if the client's currency differs. When Korapay's signed webhook confirms the charge, the wallet is credited (plus any top-up bonus, §8.3) and the client is returned to a receipt showing what they paid, the bonus and their new balance, all stated in their own currency.

If a payment does not arrive:

16.4 Customising the order form with an add-on

Panels sell different things, and sooner or later the New Order form needs a field the standard SMM types do not have — a target keyword for a ranking service, a campaign code, a market to target. That is an add-on, not a core edit: an add-on can add its own fields to the order form, and what the customer types is validated, stored with the order, shown on the order confirmation and in Admin → Orders → View details, and forwarded to your provider with the rest of the order.

An add-on can also replace a built-in control. The drip-feed block, for instance, asks for runs and an interval; an add-on can hide it and offer a single field in the language your market speaks — "views per hour" — converting it to runs and interval behind the scenes. The delivery engine is unchanged; only what the customer sees is.

Why an add-on and not a quick edit Updating the panel replaces the core folders (§3.10), so a change made directly in a core file is lost at the next update. The same change made in an add-on lives in addons/, which updates never touch — it keeps working, version after version. The hooks available, the field definition format and two complete working examples are documented in addons/README.md inside your installation.

16.5 Themes — extra designs from an add-on

A theme is an add-on that brings DESIGNS rather than a gateway. One package can carry two: one for your public landing page and one for the client portal your customers order from. They are independent — you may run a theme's landing with the portal design you already use, or the other way round.

Installing one is exactly §16.1: Admin → Add-ons → Upload Add-on → Enable. From that moment the design appears in the two pickers you already know — Admin → Landing for the public page and Admin → Appearance for the portal — next to the designs that ship with the panel, with the same Apply and Preview buttons. Preview shows it on your real content without publishing anything.

Nothing changes until you choose it Installing and enabling a theme only adds options to those two pickers. Your current design keeps rendering, your page content, your logo and your colours are untouched, and switching back is one click. A theme cannot redefine or replace the designs that came with the panel.

Your brand still wins. A theme may ship the colour it was drawn around, and that colour is used only while a panel has never been branded. As soon as you set a colour in Admin → Branding, yours is applied on that design like on any other — the panel applies it last, after the theme's own stylesheet, so a package cannot overrule it.

Resellers. A theme's designs obey the two decisions you already take: which designs a plan includes (§10) and which of those a particular reseller may use (§9). A reseller you did not grant it to never sees it in their picker, which is what lets you keep a design for a higher tier.

If it goes away. Switch the theme off, uninstall it, or move a reseller to a plan that no longer includes its design: the panel falls back to a design that panel is allowed to use, on the very next page load. No page goes blank and nothing you wrote is lost.

Updating a theme Upload the newer ZIP from the same screen, exactly as the first time. Your design choice and your content are not stored by the theme, so nothing has to be set up again, and your customers' browsers pick up the new stylesheet by themselves.
If you build themes yourself A theme declares "type": "theme" in its manifest and returns its designs from the themes.landing and themes.client hooks. The landing accepts a design built entirely from your own variables and stylesheet; the portal is styled by a compiled stylesheet the buyer never rebuilds, so a portal design names one of the three built-in skins as its base and repaints it. Your files are served straight from your package — never copy anything into public/, an update would remove it. The full contract, with a worked example, is in addons/README.md inside your installation.