Nexus SaaS · version 1.0

Run Nexus for many companies at once.

Nexus SaaS is the multi-tenant edition of Nexus. Every customer gets a workspace — its own subdomain, its own database, its own people, files and settings — and you run all of them from one super-admin panel: plans, trials, payments, support, updates and backups. This guide covers installing the platform, operating it, and everything a workspace can do.

In a hurry? Point a wildcard DNS record at the server, upload the files, open your domain and follow the installer. Then read Your first hour — the shortest path from an empty platform to a paying customer signing themselves up.

Welcome

Two products live in this one codebase, and this guide is in two halves to match.

The platform

What you run, on the apex domain: a marketing site with pricing, self-serve sign-up, and a super-admin panel for workspaces, plans, subscriptions, gateways, support, a knowledge base, monitoring, updates and backups.

The workspace

What each customer runs, on their subdomain: the whole of Nexus — HR, CRM & Sales, Support and Projects — plus three screens that talk back to you: Plan & Billing, Platform Support and Help Center.

The first half of the sidebar — Getting started, Platform panel and Inside a workspace — is written for the operator. Everything from Foundations onward describes the product a workspace's own administrator sees, and is the same text as the single-company Nexus guide, because inside a workspace it is the same product. Hand that half to your customers as-is.

What is in the box

Workspaces

One row in the central database, one MySQL database, one subdomain. Created by a visitor in under a minute or by you from the panel; suspended, re-provisioned or moved to another database server without touching the others.

Plans & trials

Monthly and yearly prices, an employee limit and a staff limit per plan, a trial length, a feature list for the pricing table. Limits are enforced inside the workspace, not just printed.

Billing

Stripe, PayPal, Paystack, Flutterwave, Mollie and bank transfer, behind one interface. A workspace admin renews or upgrades from their own settings; the webhook and the return URL both complete the payment, whichever lands first.

Support & help

Tickets a workspace raises with you (not their own helpdesk), and a knowledge base you write that every workspace reads as its Help Center.

Marketing site

Hero, features, how-it-works, pricing, stats, testimonials, FAQ, footer, SEO, privacy policy and terms — every word editable in the panel.

Operations

Health checks and a structured log of provisioning, database and payment problems; a browser-driven updater that migrates every database in turn; one-click dumps of all of them.

Platform admins

Super admins see everything; admins see the sections you tick. These accounts are platform-side only and never sign in to a workspace.

The whole of Nexus

Everything in the second half of this guide, in every workspace, in eight languages with RTL, with a REST API on every subdomain.

How to read this guide

Sections follow the order you will need them: install the platform, set it up, then each panel screen in the order the sidebar lists it, then the workspace. Every screen named here exists in the product — where something is deliberately absent it is listed under Deliberately not here rather than described as if it were built.

How it is put together

Ten minutes here saves an hour later, because almost every operational question — where is this stored, which database, which domain — has the same answer.

Central (the platform)Workspace (a tenant)
HostThe apex domain — yourplatform.comA subdomain — acme.yourplatform.com
DatabaseOne: the one the installer createdOne per workspace: nexus_tenant_acme, created when the workspace is
Who signs inPlatform administratorsThe company's staff, employees and portal clients
What it servesMarketing site, sign-up, the super-admin panelAll of Nexus, plus Plan & Billing, Platform Support, Help Center
Uploadspublic/uploads/public/uploads/t/{workspace}/

How a request finds its workspace

The host name is looked up in the central domains table. A match switches the database connection to that workspace's database before any code runs; the apex domain never matches and serves the platform; an unknown subdomain is a plain 404. After that, a gate checks the workspace's state and shows one of three holding pages instead of the app when it has to — see Trials, expiry & suspension.

What is shared and what is not

Two things share a name and are not the same thing. A workspace's Tickets are its customers' tickets; Platform Support is the workspace talking to you. A workspace's Knowledge base is its public help for its clients; the Help Center is what you wrote for them.

Server requirements

The same modest footprint as Nexus: PHP and MySQL, nothing else. There is no Node on the server, no Redis, no websockets and no Supervisor. One thing is genuinely different from a single-company install: the database user must be allowed to create databases, because every new workspace gets one.

WhatNeedsNotes
PHP8.3 or newer8.3 and 8.4 are both tested.
DatabaseMySQL 8.0+ or MariaDB 10.6+The user needs CREATE on *.* (or on nexus\_tenant\_%) so provisioning can create workspace databases. See the note on shared hosting below.
Extensionsopenssl, pdo, pdo_mysql, mbstring, tokenizer, xml, ctype, json, bcmath, fileinfo, curl, gdAll standard. The installer checks each one and names the missing ones. curl is what talks to the payment gateways.
Optionalzip, intlNice to have; nothing fails without them.
Writable.env, storage/app, storage/framework, storage/logs, bootstrap/cache, public/uploadsUsually 755 on folders, 644 on files.
Web rootPoint the domain and every subdomain at public/See Domains & DNS.
mysqldumpOn the PATH, or MYSQLDUMP_PATH in .envOnly for platform backups. Workspace-side backups are written in PHP and need nothing.

Shared hosting and the one-database-per-customer model do not mix well. cPanel-style hosts usually let a database user reach only databases created in the hosting panel, with a prefix you do not control. The platform still works there — set Hosting mode to shared in Platform settings, create each workspace's database by hand in the hosting panel, and enter its credentials on the workspace page before re-provisioning. It is a workable path for a handful of customers and a miserable one for fifty. A VPS is the right home for this product.

What you do not need

No cron job and no queue worker. Provisioning a workspace runs after the response is sent in the same PHP process, so the visitor sees a progress page while their database is built without anything listening in the background. Emails go out inline. Payment confirmation arrives on a webhook, which is the gateway calling you, not a process of yours polling them.

Domains & DNS

This is the one part of the install that a single-company Nexus never asked of you, and the one that is most often wrong. Every workspace lives on a subdomain, so every subdomain of your platform domain has to reach this application.

  1. A wildcard DNS record

    Alongside the A record for yourplatform.com, add one for *.yourplatform.com pointing at the same server. Until it propagates, a new workspace's address will not resolve even though the panel says it is ready.

  2. A virtual host that accepts the wildcard

    Apache: ServerAlias *.yourplatform.com on the same vhost, document root public/. Nginx: server_name yourplatform.com *.yourplatform.com;. One vhost, not one per customer.

  3. A wildcard certificate

    Browsers will refuse acme.yourplatform.com on a certificate issued for the apex alone. Let's Encrypt issues wildcard certificates through the DNS challenge (certbot --preferred-challenges dns -d yourplatform.com -d '*.yourplatform.com'); most managed hosts have a checkbox for it.

  4. Tell the platform which domain is the apex

    The installer takes the domain you open it on and writes it to APP_URL. The list of hosts treated as central — the ones that serve the panel rather than a workspace — is central_domains in config/tenancy.php; the first entry is the platform domain that subdomains are appended to. Change it there if your platform ever moves.

A handful of subdomains can never be registered: www, app, api, mail, admin, central, assets, static and cdn. Add your own to the list in Platform settings → Workspace defaults.

Installing

The platform installs itself in a browser. You do not need shell access, phpMyAdmin or a command line — only the DNS above done first.

  1. Upload and unzip

    Put the contents of the ZIP on your server and point the domain — with its wildcard — at the public/ folder. Everything is included; you do not need to run composer install.

  2. Open your domain

    Every address redirects to /setup on the platform domain until the install is finished. You land on the requirements check.

  3. Requirements

    Every required row must be green — the PHP version, the twelve extensions and the six writable paths. If one is not, fix it on the server and press Re-check. Nothing has been written yet.

  4. Company

    The platform name, currency and timezone, plus the environment and the site URL. The currency is what you charge in — every plan price and every payment is in it — and it is also the default currency each new workspace is seeded with. An application key is generated for you. Leave Debug mode off on a live server.

  5. Database

    Host, port, database name, username and password for the central database. Press Test connection before continuing. This is the user that will later create every workspace database, so give it the privilege now.

  6. Administrator

    Your name, email and password. This is the first super admin — a platform account, not a workspace one. It reaches every section of the panel.

  7. Review and install

    Check the summary, then press Install now. This writes .env, builds the central schema, seeds the three launch plans and the marketing site's starting copy, records your platform settings and creates your account. Under a minute; the log narrates each step and stops where a failure happened.

The install always starts from a clean central database. Existing tables in the database you name are dropped — the review step says so. It does not touch workspace databases, which are separate; but a re-install over a live platform loses the tenant list that points at them. Take a backup first if there is anything to lose.

The installer seals itself. Once it finishes, /setup redirects to the site and cannot be re-run. The seal is a file: storage/app/installed. Delete it to open the wizard again — and if /setup is ever reachable when it should not be, that file is the first thing to check.

Nothing is written until the last step. The wizard keeps your answers in the session, so an abandoned install can never leave a deployment with a rewritten .env pointing at a database that was never migrated. A clone straight out of git with an empty APP_KEY is handled too: the wizard plants one on the way in.

If the install fails

Message mentionsWhat to do
Access denied for userWrong database username or password. Go back a step and re-test.
Unknown databaseCreate an empty central database in your hosting panel and use that name. Then check that the same user can create more databases, or you will meet this again at the first workspace.
Permission denied / failed to open stream.env or storage/ is not writable. Set folders to 755 and files to 644.
Migrations ran but the expected tables are missingThe connection landed in a different schema than the one you named — almost always a stale config cache. Run php artisan config:clear and try again.
The last step never finishes under php artisan serveThe dev server restarts itself when .env changes, which is exactly what the last step does. Use Apache or Nginx for the install, or the command line.

Your first hour

A fresh install already has three plans, a marketing site with sensible copy and your super-admin account. This is the shortest route from there to a platform a stranger can sign up to and pay for.

  1. Brand the platform

    Settings → General Settings. Logo and favicon, the platform name, the company details that appear on receipts, the footer line. The Workspace defaults tab is worth a look now too: the app name, currency and language every new workspace starts with.

  2. Price the plans

    Platform → Plans. Starter, Business and Enterprise ship as placeholders. Set the monthly and yearly prices in your currency, the employee and staff limits (blank for unlimited), the trial length, and the feature bullets the pricing table shows. Deactivate any you do not sell rather than deleting them.

  3. Turn on a way to get paid

    Payments → Payment Gateways. Paste the keys for at least one gateway and tick Active. If you invoice by bank transfer, activate Bank Transfer and fill in your account details under Settings → General Settings → Bank details — that is what workspaces are shown.

  4. Register the webhooks

    In each gateway's dashboard, point the webhook at https://{workspace}.yourplatform.com/webhooks/subscriptions/{gateway}. Most gateways want one fixed URL, so use any real workspace's subdomain — the payment is matched by its own reference, not by the host. See Payment gateways.

  5. Write the marketing site

    Platform → Landing CMS. Every section of the public page is a form: the hero, the feature grid, how it works, pricing, stats, testimonials, FAQ, footer, SEO tags, and the privacy policy and terms. The starting copy is generic on purpose.

  6. Create your first workspace

    Either sign up at /register like a customer would, or Platform → Workspaces → New workspace. Watch it provision, open it, sign in as the admin you named. If the subdomain does not resolve, it is DNS, not the platform.

  7. Seed the Help Center

    Support → Knowledge Base. A category or two and a few articles — how to add people, how to upgrade, who to contact. Every workspace sees them under Settings → Help Center the moment they are published.

  8. Take a backup and see it work

    Administration → Backups → Create backup. One .sql per database. If mysqldump is not found, set MYSQLDUMP_PATH in .env now rather than on the day you need it.

  9. Add a second administrator

    Administration → Super Admins. A second super admin, or a limited admin who only sees Support Tickets and the logs. One account is one lost password away from a locked panel.

Command-line install

If you have shell access and would rather not use the wizard:

composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate

# edit .env: APP_URL (the platform domain), APP_TIMEZONE and the DB_ settings
# edit config/tenancy.php: central_domains — the first entry is your platform domain

php artisan migrate --force                       # the central schema only
php artisan db:seed --class=PlanSeeder            # Starter / Business / Enterprise
php artisan db:seed --class=LandingCmsSeeder      # the marketing site's starting copy

npm install && npm run build                      # only if you changed the CSS or JS

# tell the installer it has nothing left to do
echo OK > storage/app/installed

The command line does not create a super admin. Either open /setup before sealing it and let the wizard's last step do it, or insert one with tinker:

php artisan tinker
>>> App\Models\User::create(['name' => 'You', 'email' => 'you@yourplatform.com',
...   'password' => 'a-long-password', 'role' => 'super_admin', 'email_verified_at' => now()]);

Then, for a workspace to look at:

php artisan saas:demo --subdomain=demo            # provisions demo.yourplatform.com and fills it
                                                  # with the twelve-person demo company
                                                  # sign in: admin@demo.test / password

One thing to switch off before you go live. Delete any saas:demo workspace — it has a published password.

Running it locally

Wildcard subdomains are the only awkward part on a laptop, because the hosts file cannot wildcard.

The test suite runs on in-memory SQLite and needs nothing: php artisan test.

Dashboard

The first screen after signing in on the platform domain, and the one screen every administrator can see whatever sections they hold.

Four numbers across the top: total workspaces and how many signed up this month; subscription revenue this month against last; active subscriptions including trials; and how many unresolved logs need attention. Below them, twelve months of revenue and twelve months of sign-ups, subscriptions by status, workspaces per plan, the six most recent workspaces and the five newest unresolved problems.

Revenue is the sum of paid subscription payments by the date they were paid, whichever gateway they came through and whether a customer paid online or you recorded a bank transfer by hand. Pending and failed checkouts are never counted.

Workspaces

Platform → Workspaces. Every customer, searchable by name, contact email or subdomain, filterable by status, provisioning state and plan.

Creating one from the panel

New workspace asks for the same things the public sign-up does — company name, subdomain, contact details, a plan, and the first administrator's name, email and password — and provisions it the same way. The difference is only who is typing. Use it for customers you onboard by hand, for migrations from another system, and for anyone who would rather not put a card in on day one.

Subdomains are 3–30 characters, lower-case letters, digits and hyphens, starting with a letter. The reserved list and any taken name are refused with a message.

The workspace page

Everything the platform knows about one customer, on one screen:

BlockWhat it shows
OverviewDomain, contact email and phone, plan, registration date, trial end, the admin's email, and how many unresolved logs mention this workspace. The status badge is active or suspended; the provisioning badge is pending, provisioning, active or failed, with the last error under it.
Inside the workspace (live)User accounts, employees and staff, counted right now from the workspace's own database, next to the plan's limits. Unavailable while it is not provisioned or its database cannot be reached.
SubscriptionStatus, plan, billing cycle, trial end and paid-until date, and the last five payments, with a link to the full subscription.
Database configurationWhere this workspace's database lives — the platform's server by default, or a host, port, database name and credentials you enter. See below.

Four buttons act on the workspace. Open workspace goes to its sign-in page. Suspend turns every visitor away with a suspended page until Activate — the data is untouched, and the payment webhook still works so a renewal can land. Re-provision wipes and rebuilds the workspace database (see Provisioning). Delete removes the workspace and drops its database.

Delete and Re-provision both destroy the workspace's data. Delete drops the database; Re-provision wipes every table in it before migrating and seeding again. Neither can be undone. A customer who has stopped paying should be suspended, which costs nothing and keeps everything; delete only when you have a backup or a signed request.

Putting a workspace on another database server

By default every workspace database sits on the same MySQL server as the central one, created by the platform's own database user. The Database configuration block lets one workspace live elsewhere: a bigger customer on their own server, a customer whose contract requires a particular region, or every workspace on a shared host where the platform user may not create databases.

Enter the host, port, database name, username and password, then Test connection. If the server accepts the credentials but the database does not exist yet, Save & verify creates it. The password is stored encrypted. Then press Re-provision to build the schema there — the change of address does not move any data by itself. Reset to platform default drops the custom credentials and points the workspace back at the platform server (again, without moving data).

A failed test is written to Logs & System Health with a suggested cause: wrong credentials, nothing listening on that port, a host name that does not resolve, a firewall between the two servers.

Editing

Edit changes the name, contact details, plan and trial end date. Changing the plan here changes the limits the workspace is held to immediately; it does not create a payment or extend anything — that is the subscription.

Self-serve registration

/register on the platform domain, reached from the marketing site's Start your free trial button. A visitor picks a plan, names their company, chooses a subdomain, and gives the name, email and password of the first administrator. Nothing is charged: every plan starts as a trial of the length set on the plan.

Pressing Create workspace does three things in the central database — the workspace row, its domain, and a trial subscription — and then hands the visitor a Preparing your workspace page that polls every couple of seconds while the database is built. When it is ready, the page shows the trial end date and a Go to your workspace button to the sign-in page on their new subdomain.

If provisioning fails, the visitor sees Setup hit a snag and is told your team will fix it — and you see the failure in Logs & System Health, with the workspace marked failed and a Re-provision button waiting on its page. The visitor's account details are kept on the workspace, so re-running provisioning gives them the password they chose.

Registration deliberately does not seed the demo company. A new customer gets Nexus's reference data — a default location, departments, leave types, a pipeline, ticket categories, task statuses — and one administrator, and nothing else. Demo data is for saas:demo.

Provisioning

What happens between Create workspace and ready, and why it is built the way it is.

  1. Create the database

    CREATE DATABASE IF NOT EXISTS nexus_tenant_{subdomain} on the platform's server, with the central connection's charset and collation — or, if the workspace has custom credentials, a connection test against the database you named.

  2. Wipe and migrate

    Every table in that database is dropped, then the workspace schema (database/migrations/tenant — Nexus's own migrations) is run from scratch. The wipe is what makes a retry safe: a run that died half-way leaves nothing behind for the next one to trip over.

  3. Seed

    Nexus's reference data, then the workspace's settings — the product defaults with your Workspace defaults laid over them and the company's own name as the display name — then the administrator from the sign-up form, with their password hash carried across. The default HQ location is renamed to the company.

  4. Mark active

    The workspace's provisioning status flips to active and the holding page gives way to the sign-in screen.

The whole run happens after the HTTP response has been sent, in the same PHP process that handled the sign-up. That is what lets the platform work without a queue worker, and it is also why a slow server shows Preparing… for a little longer rather than timing out: the browser was answered in milliseconds and is only polling.

A failure at any step marks the workspace failed, stores the message on it, and writes a provisioning log with a suggested cause. Fix the cause — nearly always a database privilege — and press Re-provision on the workspace page. Until then the subdomain shows a holding page with a 503, so nothing caches it.

Re-provision is a rebuild, not a repair. It runs the same wipe as a first provisioning. On a workspace that has been in use, that is the customer's data gone. It exists for the failed-first-run case and for moving an empty workspace to another database server; for a live workspace with a schema problem, use the updater, which migrates without wiping.

Plans & limits

Platform → Plans. What you sell. Each plan has:

FieldWhat it does
Name and slugThe name is what the pricing table and the panel show; the slug identifies the plan in URLs and seeders and is derived from the name when left blank.
Monthly and yearly priceIn the platform currency. A workspace picks the cycle when it pays; the yearly price is charged once for twelve months.
Employee limitHow many accounts with the employee role the workspace may hold. Blank is unlimited.
Staff limitHow many accounts with any other role — administrator, HR, manager, sales, project manager, accountant. Blank is unlimited.
Trial daysHow long a new workspace on this plan runs before it must pay. 0 to 365.
FeaturesOne bullet per line, shown on the pricing table and the sign-up page. Text only — they describe the plan, they do not switch modules on or off.
Active and sort orderInactive plans disappear from sign-up and the pricing table but keep the workspaces already on them. Sort order is the column order.

A plan with workspaces attached cannot be deleted — deactivate it instead. The plans list shows how many workspaces each one has.

How the limits are enforced

Nexus keeps employees and staff in one users table, told apart by role, so the two limits are two counts of the same table. They are checked at the only two places a workspace creates an account — Settings → Users & Roles and People → Employees — and again when a role change crosses the line, because promoting an employee to manager uses a staff seat. A workspace at its limit sees the form refused with Your plan allows up to N employees. Upgrade your plan to add more, with a link to Plan & Billing; the form also shows a banner before they start typing. Nothing else in the product counts seats: importing attendance, seeding, provisioning and the API's self-service endpoints are never blocked.

Lowering a limit below what a workspace already uses does not delete anyone. It stops them adding more until they are back under it.

The three shipped plans — Starter (25 employees / 5 staff), Business (100 / 20) and Enterprise (unlimited), all with a 14-day trial — are placeholders with placeholder prices. Their feature bullets mention modules, but every workspace has every module; the bullets are marketing copy, and the limits are the only thing a plan actually controls.

Subscriptions

Payments → Subscriptions. One row per workspace: its plan, billing cycle, and whether it is on trial, active, expired or cancelled. Filter by any of those, or by plan or cycle; search by workspace.

Status

A subscription is trial from registration until the trial end date, active from the first payment until the paid-until date, and expired once whichever of those dates applies is in the past. Cancelled is a state you set. Expired is never stored — it is computed from the dates every time it is read, which is why a payment recorded today makes an expired workspace active with no further step.

Editing one

Plan, billing cycle, trial end and paid-until date are all editable by hand, and changing the plan here also changes it on the workspace. This is how you comp a customer a month, extend a trial for a prospect who asked, or move somebody to a plan you have retired.

Recording a payment

Record payment & extend is for money that arrived outside the gateways — a bank transfer, a cheque, a card you took over the phone. Enter the amount, how many months it buys (1–36), the method, the date it was paid and a reference, and the paid-until date moves forward by that many months. The extension starts from the current paid-until date if that is in the future, otherwise from today — so paying early does not lose the days already bought.

The payment appears in Transactions, in the workspace's own Plan & Billing history, and in the dashboard's revenue.

Cancelling

Cancel subscription marks it cancelled with today's date. The workspace is locked after the grace period exactly as if it had expired; recording a payment or a gateway renewal reactivates it.

Transactions

Payments → Transactions. Every subscription payment across every workspace, newest first, with two totals above it: paid this month and paid all-time.

Each row is a workspace, a plan, an amount, a gateway, a status and — for gateway payments — the gateway's transaction id. Three statuses:

StatusMeans
paidConfirmed. The subscription was extended when this was set. Counted in the totals.
pendingA checkout was started and the customer was sent to the gateway; neither the return nor the webhook has confirmed it yet. Abandoned checkouts stay pending — they are harmless, and useful evidence when a customer says "I tried to pay".
failedThe gateway declined it, the checkout could not be created, or the return URL found it unpaid.

Search matches the transaction id, your reference, or the workspace's name or email. Filter by status, gateway or plan.

Payment gateways

Payments → Payment Gateways. Six providers behind one interface. Paste the credentials, tick Active, save. Credentials are stored encrypted in the central database, never in .env; a saved secret shows as dots and a blank field on save means keep what is there.

GatewayCredentialsWebhook is verified by
StripePublishable key, secret key, webhook signing secretThe Stripe-Signature header against the signing secret.
PayPalClient ID, client secret, webhook IDPayPal's verify-signature API, using the webhook ID.
PaystackPublic key, secret keyThe X-Paystack-Signature HMAC.
FlutterwavePublic key, secret key, encryption key, secret hashThe verif-hash header against the secret hash. Uses the v3 API.
MollieAPI key (test_ or live_ — the prefix picks the environment)Mollie sends no signature; the payment is re-fetched from Mollie with your key before anything is believed.
Bank TransferNone — just ActiveNo webhook. You record the payment by hand under Subscriptions.

What a workspace sees

Only active gateways with complete credentials appear as payment methods on the workspace's Plan & Billing page. Bank Transfer shows the account details from Settings → General Settings → Bank details instead of a button. Deactivating a gateway removes it from new checkouts but its webhooks are still accepted, so a payment already in flight is never lost.

The webhook URL

POST https://{any-workspace}.yourplatform.com/webhooks/subscriptions/stripe
POST https://{any-workspace}.yourplatform.com/webhooks/subscriptions/paypal
POST https://{any-workspace}.yourplatform.com/webhooks/subscriptions/paystack
POST https://{any-workspace}.yourplatform.com/webhooks/subscriptions/flutterwave
POST https://{any-workspace}.yourplatform.com/webhooks/subscriptions/mollie

The endpoint lives on the workspace subdomain — the platform domain has no /webhooks — and the payment is found by the id the gateway echoes back, not by which subdomain the call arrived on. So a gateway that allows one webhook URL can point at any real workspace and serve all of them.

The webhook is deliberately outside every gate: no session, no CSRF, and it works when the workspace is suspended, locked for non-payment or still mid-update (during an update it answers 503, and every gateway retries on that). A webhook that fails signature verification is refused with a 400 and written to the logs with the gateway named — the usual cause is a signing secret pasted from the wrong environment.

Completing a payment

Two things can confirm a checkout: the customer coming back to the return URL, and the gateway calling the webhook. Either may arrive first, both usually arrive, and occasionally only one does. Completion is therefore idempotent: the first to arrive marks the payment paid, extends the subscription by one or twelve months, switches the plan if the checkout was an upgrade, unlocks the workspace and writes an info log; the second finds it already paid and does nothing. The return URL also asks the gateway directly whether the session was paid rather than trusting the redirect.

Landing CMS

Platform → Landing CMS. The public site on the platform domain, section by section. Nothing on it is hard-coded.

SectionFields
HeroEyebrow, title, description, primary and secondary buttons (text and URL). The primary button ships pointing at /register.
FeaturesSection title and subtitle, then a sortable list of features, each with a title, description and icon name.
How it worksLabel, title, subtitle, and an ordered list of steps.
PricingTitle and subtitle, and whether to show monthly, yearly or both. The plans themselves come from Plans — active ones, in sort order, with their feature bullets.
StatsA list of value/label pairs — 14 days / Free trial on every plan.
TestimonialsClient name, company and review text.
FAQQuestion and answer pairs.
Call to actionTitle, subtitle, one button.
FooterAbout text, copyright line, contact email, phone and address, five social links, and whether to show an Admin login link to the panel.
SEOMeta title, description and keywords.
Privacy policy and TermsServed at /privacy-policy and /terms-conditions. Each is a fixed set of headed paragraphs — introduction, data collection, cookies, your rights and so on — with a last-updated date. Write your own; the shipped text is a scaffold, not legal advice.

Every section has an active switch, so a page with no testimonials yet simply omits that block. Items in a list can be toggled off individually rather than deleted. The seeder that writes the starting copy runs once and never again — the moment a hero row exists it steps aside — so an update cannot put the defaults back over your words.

Branding — logo, favicon, platform name — comes from Platform settings, not from here, because the panel and the sign-in pages use the same marks.

Support tickets

Support → Support Tickets. Tickets that workspace administrators raise with you, from their Platform Support screen. Unread ones sort to the top with a count in the header.

A ticket has a number, a workspace, who opened it, a subject, a category (technical, billing, feature, other), a priority and a status. Open one to read the thread, reply, or change its status and priority.

StatusSet byMeans
openThe workspaceWaiting on you. A new ticket, or one the workspace has replied to.
answeredYour replyWaiting on them. Set automatically when you reply to an open ticket; their next reply flips it back to open.
resolvedYouDone, but they may still reply — which reopens it.
closedYouFinished. The workspace can read it but not reply; they open a new one instead.

Priority is low, medium, high or urgent. A workspace may pick the first three when opening a ticket; only you can mark one urgent, so the queue is ordered by your judgement rather than theirs.

Replies are plain text and appear under your name. There is no email notification in either direction in this version — see Mail — so the unread count on the sidebar is what tells you something is waiting.

Knowledge base

Support → Knowledge Base. Articles you write once and every workspace reads as its Help Center. Categories first (a name and an icon), then articles inside them, each with a title, a body, a sort position and a published switch.

Unpublished articles are invisible to workspaces, which is how you draft. Bodies are plain text; line breaks are kept. Each article counts its views, so you can see which questions people actually have. Deleting a category deletes the articles in it.

Filter the list by category, title or published state. Articles sort by category, then by their sort number, then by age.

Logs & system health

Monitoring → Logs & System Health. Five live checks along the top, and under them a log of every problem the platform has noticed about itself.

Health checks

CheckGreen when
Central databaseA query answers, with the round-trip time shown.
Workspace databasesEvery workspace's database exists on the server. A missing one is named — a workspace whose provisioning failed, or a database somebody dropped by hand.
Storagestorage/app is writable.
Disk spaceMore than 10% free.
RuntimeAlways — it reports the PHP and Laravel versions and the queue driver, for the support email.

The log

Not the framework's laravel.log — that still exists, and still has the stack traces — but a structured list of things an operator should act on, each with a workspace, a type, a severity, the message, any context, and a suggested cause written by whatever raised it:

TypeRaised by
provisioningA workspace build that failed, with the exception and the file it came from.
databaseA failed connection test on a workspace's custom credentials; a migration that failed during an update.
paymentA webhook whose signature did not verify (warning), and — at info — every payment that completed, so the money trail is here too.
generalA backup run that reported a database it could not dump.

Severity is info, warning or critical. A log is unresolved until you mark it resolved; the dashboard counts unresolved ones and the workspace page counts those about that workspace. Clear resolved deletes everything already handled. Filter by status, severity or type.

Super admins

Administration → Super Admins. The people who sign in on the platform domain. These accounts live in the central database and are platform-side only — they never sign in to a workspace, and a workspace's administrator never reaches here.

Two roles. A super admin reaches every section, including this one. An admin reaches only the sections you tick:

SectionOpens
workspacesWorkspaces — list, create, edit, suspend, re-provision, database settings, delete.
plansPlans.
cmsLanding CMS.
subscriptionsSubscriptions, including recording payments and cancelling.
transactionsThe transactions list.
gatewaysPayment gateway credentials.
supportSupport tickets.
kbKnowledge base.
systemLogs & system health.
settingsGeneral settings, mail, email templates, and platform backups.
updatePlatform update.
adminsThis screen.

The dashboard is open to everybody. Unticked sections disappear from the sidebar and answer 403 when opened by URL — the same rule as inside a workspace. A support person who should only see tickets and the logs gets exactly those two boxes.

The last super admin cannot be demoted, so the panel can never lock itself out.

Platform settings

Settings → General Settings. Eight tabs. Each saves only its own fields.

TabWhat lives there
GeneralPlatform name; the currency code and symbol you charge in; the default language and which of the eight languages the switcher offers on the panel and the marketing site.
BrandingLogo and favicon, used on the panel, the sign-in screens and the marketing site. Stored in public/uploads/branding; removing one restores the built-in mark.
AppearanceWhether the platform name shows beside the logo, whether the theme customizer button appears, and the footer line at the bottom of every panel page.
CompanyWho runs the platform — legal name, phone, email, website, address. Used on receipts.
Workspace defaultsWhat a new workspace is seeded with: default app name, language, currency code and symbol, and a default favicon. Also the reserved subdomains list, and a switch to seed demo data into new workspaces (stored for a future seeder — nothing reads it yet). Existing workspaces are never touched by changes here.
RemindersWhen workspaces should be warned that a subscription or trial is ending: days-before lists, delivery channels, how far ahead the in-app banner shows. Stored but not yet delivered — the screen says so. See Deliberately not here.
Bank detailsBank name, account holder, account number, IBAN, SWIFT, branch, and free-text payment instructions. Shown to a workspace that chooses Bank Transfer on its billing page.
SystemHosting mode (VPS or shared); the environment, debug flag and application URL, written straight to .env; and a Clear cache button that flushes config, routes, views and the cache store.

The System tab edits .env. A wrong application URL or a typo in the environment can take the whole platform — every workspace — offline until somebody with shell access fixes the file. Change with care, and keep debug mode off in production: it shows stack traces to everybody, including your customers' customers.

The grace period — how many days after expiry a workspace stays usable — is not on this screen. It is the grace_days setting, default 7, and is read from the central settings table; see Trials, expiry & suspension.

Mail & email templates

Settings → Mail Settings stores the platform's SMTP details — from name and address, host, port, encryption, username and an encrypted password. Settings → Email Templates lists templates by trigger, each with a subject, body, placeholders and an active switch.

In this version both screens are storage, not delivery. The platform sends no email of its own yet — no welcome on registration, no receipt, no trial reminder, no ticket notification — and ships no templates, so the templates screen is empty until a later release adds triggers. The values are kept so that release needs no re-entry. This is listed under Deliberately not here.

Mail inside a workspace — leave approvals, invoices, ticket replies to the workspace's own customers — is Nexus's own mailer, configured under the workspace's Settings → Notifications and described under Notifications & email. Each workspace's server is stored in that workspace's own settings, password encrypted, and is swapped into the mail config only while that workspace is being served. A workspace that has not chosen a server — or that picks Platform default — inherits the MAIL_* values in .env, which is how you offer every customer a working relay without configuring each one: set the platform's SMTP details there, and let the customers who want their own domain on the envelope enter their own.

Platform update

Settings → General → Updates. After new code is on the server, this walks the central database and then every workspace database through their pending migrations, one at a time, from the browser.

  1. Back up

    Administration → Backups. The update screen reminds you; do it.

  2. Copy the new files over

    Overwrite everything except .env, public/uploads/ and storage/. Those are your data and every workspace's.

  3. Press Start update

    The platform goes into maintenance: the panel, every workspace subdomain and the marketing site all answer 503 (a page for browsers, JSON for the API and for webhooks, which retry). Only the updater itself and the sign-in it needs stay reachable. The lock is a file, storage/app/platform-update.lock, on purpose — the databases are the very thing being migrated.

  4. Watch it run

    Central first, then each provisioned workspace oldest-first, each as its own request so a slow tenant can never time the whole run out. Every target shows pending, running, done, skipped or failed with the migration output under it. Workspaces still pending, provisioning or failed are skipped — they have no schema to migrate.

  5. Finish

    Config, route, view and application caches are cleared and the lock is released. Hard-refresh the browser once.

Keep the tab open until it finishes: the browser drives the run. If it dies half-way — the laptop closed, the network dropped — the maintenance lock stays on. Come back to the screen, which says so, and either Re-run update (migrations that already ran are skipped, so this is safe) or Release maintenance lock if you are done. A target that fails is written to the logs and the run continues to the next, so one broken workspace never holds the other forty hostage.

From the command line the same thing is php artisan migrate --force then php artisan tenants:migrate, without the maintenance page.

Platform backups

Administration → Backups. Create backup writes one timestamped folder under storage/app/backups holding a .sql file for the central database and one for every workspace database. The list shows each run's files and size; download any single dump, or delete a run.

Unlike the backup inside a workspace, this one shells out to mysqldump — it is the one tool guaranteed to restore cleanly into the same server, and a platform host is not the kind that disables exec(). It is found on the PATH, then next to the PHP binary (where Laragon and XAMPP keep it); set MYSQLDUMP_PATH in .env to be explicit. The password travels in the environment, not on the command line.

A database that could not be dumped is skipped, marked in the output, and written to the logs — so a zero exit code is not the whole story and the screen tells you when a run finished with errors. Workspaces on custom database servers are dumped with the platform's credentials against the platform host in this version; back those up from their own server.

php artisan nx:backup                 # everything
php artisan nx:backup --tenant=acme   # one workspace, by subdomain

The backup covers the databases, not the uploads. Platform branding, landing images, and every workspace's logos, avatars, CVs, contracts and attachments live under public/uploads/. Copy that folder too, and .env. There is no restore button, deliberately; restoring is mysql -u USER -p DATABASE < file.sql, one database at a time.

What a workspace is

Everything from Foundations onward in this guide, exactly as written, with its own sign-in page at acme.yourplatform.com/login, its own administrator, its own users and roles, its own settings, uploads, API tokens and languages. The customer's administrator has Nexus's Administrator role, which bypasses every permission check inside the workspace — and no standing whatsoever on the platform domain.

Three screens are added to the workspace's Settings group, and one banner. They are the only places a workspace and the platform touch:

Plan & Billing

Administrators only. The current plan and seat usage, payment history, and self-checkout for renewals and upgrades.

Platform Support

Any signed-in user. Tickets to the platform operator, separate from the workspace's own helpdesk.

Help Center

Any signed-in user. The platform's knowledge base, read-only.

The seat banner

On the user and employee forms, when a plan pool is full — with an Upgrade plan button for administrators.

Inside a workspace the REST API answers on the same subdomain (acme.yourplatform.com/api/v1/…), tokens are per workspace, and the API section applies unchanged.

Plan & Billing

Settings → Plan & Billing, inside the workspace. Administrators only — it is the one page ordinary staff never need. Three things on it.

Current plan and usage

The plan, its status (trial, active, expired or cancelled), the date the trial ends or the paid period runs to, and two meters: employees used against the employee limit, staff against the staff limit. Blank limits read as unlimited.

Renew or change plan

Pick a plan (the current one to renew, another to change), a cycle — monthly or yearly — and a payment method. The methods are whichever gateways you have activated. Choosing a card gateway creates a pending payment and sends the administrator to the gateway's hosted checkout; on return the payment is verified with the gateway and, if paid, the subscription is extended by one or twelve months from its current end date (or from today if it had lapsed), the plan is switched if they chose a different one, and any non-payment lock is lifted on the spot. A cancelled checkout comes back to the page with nothing changed.

Choosing Bank Transfer shows your bank details and payment instructions on the page. Nothing is recorded until you record the payment in the panel; the workspace sees it in its history at that moment.

Payment history

The last twenty payments — date, amount, method, status, plan and reference — including the ones you recorded by hand and any pending or failed checkouts.

Platform Support

Settings → Platform Support, inside the workspace. Any signed-in user of the workspace can open a ticket with the platform: a subject, a category (technical, billing, feature request, other), a priority (low, medium or high) and a message. They get a ticket number and a thread; you get it under Support Tickets marked unread.

Replies from you show in the thread and flag the ticket unread for them. They can reply until you close it; a closed ticket says so and points them at opening a new one. Opening a ticket is written to the workspace's activity log.

This is deliberately not under the workspace's Support module, and its data lives in your database, not theirs — it is the customer talking to you, and it would be wrong for it to appear in their own helpdesk's queue or SLA reports.

Help Center

Settings → Help Center, inside the workspace. The published articles from your Knowledge base, grouped by category; categories with nothing published are omitted. Read-only, available to every signed-in user, and each read counts towards the article's view count in your panel.

Trials, expiry & suspension

Every request to a workspace passes one gate after its database is found, and the gate can answer with one of three pages instead of the app:

PageStatusWhenWho fixes it
Still being provisioned503Provisioning is pending, running or has failed. The 503 stops anything caching it.Nobody, usually — it clears itself in under a minute. If it failed, you, with Re-provision.
Suspended403You pressed Suspend.You, with Activate.
Subscription expired402The subscription is expired or cancelled and the grace period has passed.The workspace admin, from Plan & Billing; or you, by recording a payment or extending the dates.

The API gets the same answers as JSON with the same status codes.

The grace period

A subscription that runs out does not lock the workspace that minute. grace_days — default 7 — is how long after the trial end or the paid-until date the workspace keeps working as normal. That is the window in which a customer who meant to renew can do so without their team noticing anything.

When the grace period passes, the workspace is locked, but not bricked. Sign-in, sign-out, password reset, the language switcher and the whole of Plan & Billing stay reachable, so the administrator can sign in, see the expired page with a Renew subscription button, pay, and be back inside in the same session. Everybody else sees the same page with contact your administrator. The data is never touched by any of this.

A workspace with no subscription row at all — one created by hand in the database, say — is never locked for billing. Bookkeeping must never take a customer down.

Suspension versus expiry

Expiry is the customer's to fix; suspension is yours. Use Suspend for a dispute, an abuse report or a customer who has asked you to pause — it does not care about payment state, and a payment does not lift it. The webhook still works while a workspace is suspended, so a renewal that lands during a suspension is banked and applied when you activate them again.

Around the screen

Four things sit outside the modules and are worth knowing before you start. They are the same on the platform panel and inside a workspace.

WhereWhat it does
Search, in the headerInside a workspace, one box across every module: employees, candidates, job openings, leads, deals, contacts, companies, invoices, tickets, projects and tasks. Results are filtered by what you may see — a sales user searching a name never gets the candidate record.
The bellEvery notification raised for you, newest first. Opening one marks it read and jumps to the record. Mark all read clears the count.
CalendarOne month view fed by every module: approved leave, holidays, training sessions, interviews, birthdays, CRM activities, invoice due dates, task due dates and milestones. Sources you lack permission for are never queried.
The customizerThe paintbrush button, bottom right. Accent colour, light/dark/system and corner radius, saved in your own browser — it changes nothing for anybody else. Hide the button in Settings → General Settings (either panel).

The language switcher is in the user menu, alongside your profile and sign out. On the platform domain it offers the languages ticked in Platform settings; inside a workspace, whichever are switched on in that workspace's Settings → Languages.

Everything from here to Workspace backups describes the product inside a workspace — the same text as the single-company Nexus guide, because it is the same product. "Settings" in these sections means the workspace's own settings, and "administrator" means the workspace's administrator.

General settings

Settings → General Settings. Three tabs. Each saves only its own fields, so correcting the currency cannot blank the company address.

TabWhat lives there
CompanyApplication name, legal name, email, phone, website, address and tax number; logo and favicon; default language and which languages are offered; timezone, date format and the first day of the week; the dashboard footer line; whether the site name shows beside the logo; whether the customizer button appears.
Money & invoicingCurrency code, symbol, decimal places and whether the symbol sits before or after the number; the default tax percentage; the quote and invoice number prefixes; default payment terms in days; the terms text and footer that print on every document.
APIThe rate limit, in requests per minute per token. See REST API.

The legal name, address and tax number are what appear on printed quotes, invoices and contracts — not the application name, which is the label in the sidebar.

Changing the invoice prefix starts a fresh numbering run. The counter is kept per prefix, so switching from INV- to 2026- begins at 0001 again and leaves every existing invoice number untouched. That is usually what you want in January, and never what you want mid-year.

Module settings live on their own screens, because each is a set of records rather than a set of switches: HR Settings, CRM Settings and Project Settings.

Locations

Settings → Locations. An office, a site, a country. Every person belongs to one, and one location is the default that new people get.

Locations matter in three places: holidays are declared per location, so a Turkish office and a German one keep their own calendars; leave day counting skips the holidays of the requester's location; and attendance and headcount reports can be filtered by location.

A location cannot be deleted while people are attached to it — move them first.

Users & roles

Settings → Users & Roles. The same list as Employees, seen from the access side: who can sign in, as what, and with which sections open to them.

Seven roles ship. A role is a starting point, not a cage — it sets a sensible set of permissions when you create somebody, and you tick or untick from there.

RoleOpens by default
AdministratorEverything. This role bypasses the permission check entirely rather than being handed every box.
HREmployees, attendance, leave, payroll, recruitment, performance, reports.
ManagerEmployees, attendance, leave, performance, projects, reports.
SalesCRM, sales, support.
Project managerProjects, support, reports.
AccountantPayroll, sales, reports.
EmployeeNothing beyond self-service — which every signed-in person has.

Seats are counted against the workspace's plan. Employees are one pool and every other role is the staff pool. When a pool is full the form is refused with an upgrade prompt and this screen shows a banner beforehand; changing somebody's role from employee to any staff role uses a staff seat. See Plans & limits and Plan & Billing.

Deactivating somebody (the toggle on the row) stops them signing in immediately and kills their API tokens with them, without deleting anything they did. That is the right move when a person leaves: their name stays on the deals they won and the tasks they finished.

Permissions

Access is by section, not by screen. Twelve sections exist, and each one is a tick box on a person's record:

SectionOpens
employeesEmployees, org chart, departments, designations, onboarding, documents, announcements, assets.
attendanceAttendance, timesheets, shifts and schedules, holidays.
leaveLeave requests and leave types.
payrollPay runs, salary structures, loans, expense claims.
recruitmentJob openings, candidates, interviews.
performanceGoals, reviews, training, warnings.
crmLeads, deals, contacts, companies, activities, campaigns, templates, forms.
salesProducts, quotes, invoices, payments, contracts.
supportTickets and the knowledge base.
projectsEvery project, all tasks, time tracking, workload, templates.
reportsHR, sales and project reports, and the activity log.
settingsGeneral settings, locations, users, module settings, notifications, API tokens, languages, backups.

What everybody gets

Self-service needs no section. Every signed-in person can see their own attendance, book their own leave, read their own payslips, claim their own expenses, fill in their own timesheet, track their own goals and see the tasks assigned to them — scoped to themselves, always. See My workspace.

Approvals are the other unpermissioned door: anybody who has people reporting to them gets an Approvals screen for their own reports, without being handed the whole HR module.

Projects are the one exception to section-only access. Without the projects section a person still reaches the projects they are a member of — a developer needs their board without being handed the portfolio. Viewers see, members work, owners and managers run the project. See Projects.

Hiding a menu item is presentation; the middleware on the route is the actual guard. A person who types a URL they may not have gets a 403, not the page.

Departments & designations

People → Departments and People → Designations.

A department has a name, an optional head and an optional parent — the parent is what lets the org chart draw a tree rather than a row. A designation is a job title, and it is what shows under a person's name across the whole product.

Neither can be deleted while somebody is in it. Both are used as filters on almost every HR list and report, which is the real reason to keep them tidy.

Employees

People → Employees. The directory, and the record every other HR screen hangs off.

Adding somebody

Name, email and role are required; everything else can follow. The fields that matter later:

FieldUsed by
ManagerWho approves this person's leave, timesheets and expense claims, and where they sit on the org chart.
Department & designationFilters and grouping across every HR list and report.
LocationWhich holiday calendar applies to their leave and attendance.
Hired onProrates a first-year leave allowance and drives length-of-service figures.
Employment typeFull time, part time, contract, intern or freelance. Shown throughout; contract end dates are surfaced on the dashboard.
Probation ends onSet automatically from the hire date and the probation length in HR settings; editable.
Left onSetting it takes the person out of headcount, rosters and pay runs without deleting their history.

The employee code is generated: a prefix from settings plus the next free number, zero-padded. Codes already handed out are never reused, so a code always points at one person for the life of the system.

The employee record

Tabs across the top: profile, attendance, leave, documents, assets, salary, goals and reviews. Everything on those tabs is the same data the module screens show, filtered to this person — there is no second copy.

Salary is its own screen, and needs the payroll section: the basic amount, the pay frequency and the components attached to this person. See Salary structures.

Prefer Left on and the active toggle to deleting a person. Deleting is offered, but a person who has payslips, approved leave and won deals is woven through the history. Marking them as left ends their employment everywhere the system cares while keeping every record readable.

Org chart

People → Org Chart. The reporting tree, drawn from the manager on each person's record. Click a node to open the person.

Anybody with no manager is a root. If the chart looks like a lawn rather than a tree, somebody's manager field is empty.

Onboarding

People → Onboarding. Checklists for a new starter — the laptop, the accounts, the contract, the first-week introductions.

Build a template once as a list of tasks, then assign it to a person. That copies the tasks onto them with due dates counted from their start date. The screen shows every open checklist and what is outstanding on each; tick items off as they happen. A candidate hired through recruitment can have a checklist attached at the moment they are converted into an employee.

Documents

People → Documents. Every file attached to a person, in one list — contracts, certificates, ID copies, right-to-work papers — each with a category and an optional expiry date.

Expiry is the point of the screen. Filter to expiring within 30 days or already expired, and the counts at the top say how many of each there are, so a visa or a certification does not lapse quietly.

Documents are uploaded on the person's own record, not here — this screen is the overview across everybody. Files are stored under public/uploads/ and are reachable only through the app.

Announcements

People → Announcements. Company notices, optionally scoped to a department or a location and optionally pinned. Published announcements appear on the dashboard for the people they are aimed at; drafts appear to nobody.

Company assets

People → Company Assets. Laptops, phones, keys, cars — anything issued to a person and expected back.

An asset has a name, an asset tag, a category, a serial number, a purchase date and a cost. Two buttons drive it: Assign hands it to somebody from a date, and Return takes it back. Each assignment is kept, so the asset shows every person who has held it and the person shows what they currently hold — which is the list you want on a leaver's last day.

Working time rules

Settings → HR Settings. Five tabs of policy that the rest of the HR module reads. Set these before you start recording anything, because they change what recorded data means.

TabSettings
Working timeWhich days of the week are working days; the standard start and end time; grace minutes before a clock-in counts as late; the overtime multiplier used by payroll; whether attendance may be entered by hand as well as clocked.
Leave & probationThe month the leave year starts, and the default probation length in months.
Payroll & expensesThe pay day of the month, the note printed on every payslip, and the list of expense claim categories.
Careers pageWhether the public careers page is on, and the introduction shown at the top of it.
ReviewsThe default question set handed to every new review cycle.

The leave year start month is the one to get right first. It decides which year a request is charged to, when balances reset and when carry-over is calculated. Changing it after balances exist does not restate them.

Attendance

Time & attendance → Attendance. A month at a time, everybody down the side, days across the top, one coloured cell each.

How a day gets recorded

Four actions make a day: in, break start, break end, out. People clock themselves from My Attendance or through the API; HR can also type a day in directly if allow manual attendance is on. Every row records where it came from, so a hand-entered day is distinguishable from a clocked one.

When the day is closed, the worked minutes are computed as the span between in and out minus any breaks, and the day is marked against the schedule: present, late (past the start time plus the grace minutes), half day, absent, on leave or a holiday. That status is what the grid colours and what payroll later reads.

The grid

Click any cell to open that person's day and correct it. The screen also does the two bulk jobs: Export writes the month out as CSV, and Import takes a CSV from a biometric device or a spreadsheet.

The import wants a header row. Identify the person with either employee_code or email, and give at least a date:

employee_code,date,clock_in,clock_out,break_minutes,status,note
EMP-0004,2026-08-24,09:02,17:35,45,,
EMP-0007,2026-08-24,,,,absent,Called in sick

Rows naming nobody, or with no date, are skipped rather than guessed at, and the message afterwards says how many landed and how many did not. Imported rows are marked as such, so they stay distinguishable from clocked ones.

Approved leave and holidays are drawn into the grid automatically. Nobody has to mark a public holiday as an absence.

Timesheets

Time & attendance → Timesheets. One row per person per week: the minutes worked each day, the week's total, and the overtime in it.

Press Build to generate the week's sheets from the attendance already recorded — for everybody, or for one person. Building again refreshes any sheet still in draft or rejected, and leaves submitted and approved ones alone, so it is safe to press twice.

A sheet moves draft → submitted → approved or rejected. People submit their own from My Timesheet, where they can also correct the daily figures before sending them; managers decide from here or from Approvals.

Only approved timesheets feed payroll. The overtime minutes on an approved sheet are what a pay run pays at the multiplier; a sheet still sitting in draft pays nothing.

Shifts & rosters

Time & attendance → Shifts & Schedules. A shift is a named pattern — a start, an end, a break allowance and which days it covers. Everybody who has no shift assigned follows the company default from HR settings.

The roster view assigns people to shifts across a week. Assigning changes what "late" means for that person and what a full day of theirs is worth, which is how a part-time or night-shift employee stops looking permanently absent.

Holidays

Time & attendance → Holidays. Public holidays, per year, per location. A holiday can apply to every location or just one.

Holidays are subtracted everywhere it matters: they are not working days for leave counting, they are not absences on the attendance grid, they do not cost pay, and the workload chart does not expect anybody to be at their desk. Load next year's before December — leave requests that straddle the new year need them.

Leave

Time & attendance → Leave Requests and Leave Types.

Leave types

A type is the policy. Each carries:

FieldWhat it does
Days per yearThe full entitlement. Zero means unlimited-but-tracked — requests are recorded and never blocked by a balance.
PaidUnpaid leave costs a day of pay per day taken in the next pay run. Paid leave does not.
AccrualYearly hands over the whole entitlement at the start of the leave year. Monthly releases a twelfth per month worked, so a new starter accumulates rather than beginning with a full year in hand.
Carry-over maxHow many unused days may cross into the next leave year. Zero means use it or lose it.
Requires attachmentForces a file on the request — a sick note, for instance.
Minimum notice daysBlocks a request filed later than this many days before it starts.

Either accrual prorates a hire year: somebody who joins in September does not get twelve months of holiday.

Balances

A balance row exists per person, per type, per leave year, and is created the first time it is needed. It holds what was allocated, what was carried in, what has been taken and what is pending. Two buttons on the Leave Types screen manage them: Allocate recalculates entitlements for a year, and Adjust writes a manual correction against one person with a reason — which is how you handle a one-off grant without lying about the policy.

Requesting and deciding

People file from My Leave: a type, a date range, an optional half day, and a reason. Nexus counts the working days in the range, skipping weekends and the holidays at that person's location — a half day on a single date counts as 0.5 — and refuses the request if the balance will not cover it or the notice is too short.

The request goes to the person's manager, who approves or declines it here, from Approvals, or from the email. On approval the days move from pending to taken, the attendance grid fills in, and the leave lands on the shared calendar so nobody schedules a workshop into it. The requester is notified either way, and may cancel a pending request themselves.

The Leave calendar tab shows the whole team's booked leave in a month view — the screen to check before approving anything.

Salary structures

Payroll → Salary Structures defines the components; each person's own structure is on their record, under Salary.

A component is an earning or a deduction, calculated either as a fixed amount or a percent of basic. Housing allowance, transport, a pension contribution, a union fee — you define the vocabulary your country uses.

A person's structure is their basic amount, a pay frequency (monthly, weekly or hourly) and the components attached to them with a value each. That is everything payroll needs about them; the rest it reads from the clock.

There is no tax engine. Income tax and social contributions change by country and by year, and a table that is subtly out of date is worse than none. Model them as deduction components with the rates you are told to apply — a percent of basic, or a fixed amount — and they will appear on every payslip.

Pay runs & payslips

Payroll → Pay Runs. A run covers a period and a set of people, and moves draft → approved → paid.

  1. Create the run

    Pick the period start and end. Nexus takes the next reference number and generates a draft payslip for every employed person with a salary structure.

  2. Read the payslips

    Each is built from the structure, the attendance and leave in the period, the timesheets approved in it, the expense claims approved and not yet reimbursed, and the loans being repaid. Every line shows where it came from.

  3. Fix and regenerate

    Corrected an absence or approved a late timesheet? Press Regenerate and the draft is rebuilt from current data. Only a draft can be regenerated.

  4. Approve

    Freezes the figures. Nothing recalculates after this point.

  5. Pay

    Marks the run paid, stamps the payslips, reduces the loan balances by the instalments taken, and marks the reimbursed expense claims as paid. The payslip_ready notification goes out if it is switched on.

What a payslip is made of

LineHow it is calculated
Basic payMonthly takes the basic as-is. Weekly scales it by the days in the period. Hourly multiplies it by the minutes actually worked in the period.
ComponentsEach earning or deduction on the person's structure — a fixed amount, or a percentage of the basic just computed.
OvertimeOvertime minutes from timesheets approved within the period, at the hourly rate times the multiplier from HR settings.
Expense reimbursementEvery approved, unpaid expense claim for that person, added as an earning.
Unpaid leave & absenceUnpaid leave days and recorded absences, each costing one day's pay.
Loan instalmentsThe next instalment of every active loan that has started, as a deduction.

The day rate is the basic divided by the working days in the period — weekends and that person's location holidays excluded — and the hourly rate is the day rate over their scheduled hours. Nothing is rounded until the payslip is written, so the lines add up to the total exactly.

Gross is the sum of the earnings, deductions the sum of the deductions, and net the difference. Print gives a payslip laid out for A4 or PDF; Email payslips sends each person theirs; and everybody can read their own from My Payslips whether or not you email them.

Loans & advances

Payroll → Loans & Advances. A loan has a title, an amount, a number of instalments and a start date. From that start date, each pay run deducts one instalment and reduces the remaining balance when the run is paid.

A salary advance is the same record with one instalment. The person's payslip always shows what was taken and what is left, so nobody has to keep a side spreadsheet.

Expense claims

Payroll → Expense Claims. Staff file claims from My Expenses: a category, an amount, the date it was spent, a note and a receipt.

A claim runs pending → approved or declined → paid. The manager or payroll decides; the claimant is notified. A claim can be marked paid by hand here, or left to the next pay run, which reimburses every approved unpaid claim on the payslip and marks them paid when the run is paid. Either way a claim is only ever paid once.

The categories come from HR Settings → Payroll & expenses.

Jobs & candidates

Talent → Job Openings and Candidates.

An opening has a title, a department, a location, an employment type, a description, a number of seats and a status. Published openings appear on the careers page; drafts and closed ones do not.

Candidates sit on a kanban by stage — new, screening, interview, offer, hired, rejected — and move by dragging. Each records a source (careers page, referral, LinkedIn, job board, agency, other), so you can see which channel actually produces hires. A CV and any other files attach to the record, and a rating out of five can be set from the card.

Interviews & hiring

Talent → Interviews. Book an interview against a candidate: a date and time, a length, a place — a room or a meeting link — and a panel of interviewers. Everybody involved is notified and the interview appears on the shared calendar. The interview itself runs scheduled → completed or cancelled.

Afterwards each member of the panel leaves their own feedback: a rating, written comments and a recommendation of hire, hold or reject. One entry per interviewer, so the decision is made against something written down rather than the loudest memory in the room.

Hiring

Press Hire on a candidate and Nexus creates the user account, allocates the employee code, attaches an onboarding checklist if you choose one, moves the candidate to hired, and counts a seat filled on the opening. The candidate record stays, linked to the person, so you keep the whole history of how they arrived.

Careers page

A public page at /careers, outside the login, listing every published opening. Each has its own page with the description and an application form: name, email, phone, a message and a CV upload. A submission creates a candidate at the new stage with the source set to the careers page.

Switch the page on or off, and write the introduction that sits above the list, in HR Settings → Careers page. The page follows the site theme and the visitor's language.

Goals & OKRs

Talent → Goals & OKRs. A goal belongs to a person, has a title, a description, a due date and a progress percentage, and runs active → completed or cancelled.

Under a goal sit its key results — the measurable parts. Each has a title, a target, a current value and a unit, so "grow trial sign-ups" becomes "trial sign-ups: 340 of 500". Moving a key result's current value is what moves the goal's progress; nobody types the percentage.

Goals can also nest: give one a parent and a company objective carries the team goals underneath it, each with its own owner. The goal page shows the children and where each has got to.

People update their own from My Goals. Goals appear on the employee record and feed the review conversation rather than being scored automatically — the number is a prompt, not a verdict.

Performance reviews

Talent → Reviews. Reviews happen in cycles: a name, a period, a due date and a question set (the default comes from HR settings, and each cycle can differ).

Opening a cycle hands every employee two forms — a self review for them and a manager review for their manager. Each question is rated one to five with room for comment. A submitted form is scored as the mean of its ratings, so a person's cycle shows their own score beside their manager's, and the gap between the two is usually the conversation worth having.

The cycle screen tracks who has submitted and who has not.

Training

Talent → Training. A course or session: a title, a trainer, a date, a location, a cost and a description.

Add attendees from the training record; mark who actually attended afterwards. Each person's completed training shows on their employee record, which is what a certification audit asks for.

Warnings

Talent → Warnings. Disciplinary records: the person, a severity — verbal, written or final — the date it was issued, who issued it, and the reason.

A warning can be acknowledged by the employee, which timestamps that they saw it. Warnings need the performance section — they are not on the employee record for a colleague with only the employees permission to read.

My workspace

Every signed-in person has this group, whatever their role. Everything in it is scoped to them; there is no way to reach somebody else's row through these screens.

ScreenWhat it does
My AttendanceClock in, clock out, start and end a break. Below, this month's own grid and the running weekly total.
My LeaveBalances per type for the leave year, the history of requests, a new request form, and cancel on anything still pending.
My PayslipsEvery payslip from a paid run, with the printable version.
My ExpensesFile a claim with a receipt; watch it move to approved and then paid.
My TasksEvery project task assigned to this person, across all projects, by due date.
My TimesheetThis week's daily minutes, editable, then submit for approval.
My GoalsOwn goals, with a slider to move progress.
ApprovalsWhat is waiting on this person as a manager — see below.

Approvals

My workspace → Approvals. One queue with three tabs: leave requests, timesheets and expense claims filed by the people who report to this person.

This is the screen a line manager lives in, and it needs no HR permission at all — managing people is not the same as running HR. Decisions taken here are identical to those taken from the module screens: the same rules, the same notifications, the same entries in the activity log.

Leads

CRM → Leads. A lead is somebody who is not yet a contact: a name, a company name as they said it, an email, a phone, a source, a status and an owner.

Statuses (new, contacted, qualified, unqualified by default) and sources are yours to define in CRM Settings. Email sends one of your templates to the lead and files the send as an activity.

Importing

Import takes a CSV with a header row, and lets you set an owner and a source for the whole file. These columns are recognised; anything else is ignored:

name,email,phone,company,job_title,status,source,value,notes

Export writes the filtered list back out in the same shape.

Converting

Convert turns a lead into a contact, optionally a company, and a deal at the top of a pipeline, in one step. The lead is kept and points at all three, so the source and the campaign it came from survive into the reports — which is the only way to answer "which channel produces revenue" rather than "which channel produces leads".

Deals & pipelines

CRM → Deals. A deal is an opportunity: a title, a value, a company, a contact, an owner, an expected close date and a stage.

Pipelines

Settings → CRM Settings → Pipelines & stages. A pipeline is an ordered set of stages, each with a colour and a probability. New pipelines start with the standard six — New 10%, Qualified 25%, Proposal 50%, Negotiation 75%, Won 100%, Lost 0% — and you rename, reorder, add and delete from there. One pipeline is the default; a company with a direct and a channel motion runs two.

The probability is what makes the forecast mean something: weighted pipeline is the sum of each open deal's value times its stage probability. Move the stages, and the forecast moves with them.

Working the board

The board view is the pipeline as kanban — drag a deal to move it, column totals at the top. Won and Lost close a deal; losing asks for a reason from the list in CRM settings, which is what turns "we lose a lot" into "we lose on price". Reopen puts a closed deal back. Every move is logged and the owner is notified when a deal is handed to them.

A deal's page carries its activities, quotes, comments and files, so the whole story of an opportunity is on one screen.

Contacts & companies

CRM → Contacts and Companies.

A company is the organisation: name, industry, website, phone, address, owner. A contact is a person, optionally at a company, with a job title, email and phone. Deals, quotes, invoices, contracts and tickets all point at one or both, which is what makes a company page a full account view — everything sold, billed and asked, in one place.

Two buttons matter on a contact. Email sends a template and files the activity. Portal access gives that contact a password for the client portal; revoking it shuts the door without touching anything else.

Activities

CRM → Activities. Calls, meetings, emails and to-dos, each against a lead, contact, company or deal, with a due date and an owner.

Open activities with a due date appear on the shared calendar and on the dashboard of whoever owns them. Marking one complete stamps it and drops it out of the queue. The timeline on a deal or contact is these records in date order — the answer to "when did we last talk to them".

Campaigns

CRM → Campaigns. A named marketing effort: a type, a period, a budget, the actual cost so far, an owner and a status. Leads carry the campaign they came from, so a campaign's page shows what it produced — leads, deals created, deals won and the revenue against the spend.

Nexus does not send bulk mail — see Deliberately not here. A campaign here is the record you attribute against, not a mail blaster.

Email templates

CRM → Email Templates. Reusable subjects and bodies for the Email button on a lead or a contact, and for canned replies on tickets.

Merge fields are written in double braces:

{{contact.first_name}}   {{contact.last_name}}   {{contact.email}}
{{company.name}}
{{deal.title}}           {{deal.value}}
{{user.name}}            {{user.email}}
{{app.name}}

Unknown fields render as nothing rather than leaking the placeholder into a customer's inbox. Preview shows the template filled with real data before you send it to anybody.

Web forms

CRM → Web Forms. A hosted form that creates a lead. Choose the fields, the source to stamp on submissions, and who owns what comes in.

Each form gets a public URL with an unguessable key — share the link, or drop it in an iframe on your marketing site. Regenerate issues a new key and kills the old link, which is what you press when a form starts collecting rubbish.

Products & services

Sales → Products & Services. A catalogue of what you sell: a name, an SKU, whether it is a product or a service, the unit it is sold in, the price, your cost and a tax percentage. Quote and invoice lines are picked from here — choosing one fills the description, price and tax so nobody retypes them, and the cost is what a margin is measured against.

Lines can also be typed freehand, so a one-off does not need a catalogue entry.

Quotes

Sales → Quotes. A quote is a numbered document for a company or contact, optionally against a deal, with lines, a discount, tax and a validity date.

The line editor

Add lines, pick a product or type your own, set quantity, price, discount and tax. The running total updates as you type, and it is computed exactly as the server will compute it: a document-level discount is spread across the lines pro rata, and tax is charged on what remains after it. The number you watch is the number that gets stored.

The life of a quote

Draft → sent → accepted, declined or expired. Send emails it to the client contact and marks it sent. Print gives the A4 document with your logo, legal name, terms and footer. Duplicate copies it for the next near-identical job.

Clients respond in one of two ways: the public link on the emailed document, or the client portal. Either way accepting or declining stamps the quote and tells the owner.

Convert to invoice copies the accepted quote — lines, discount, tax and all — into a draft invoice, and links the two.

Invoices

Sales → Invoices. Same document, same line editor, plus money coming in.

An invoice is draft, sent, partial, paid, overdue or cancelled. Sent is what starts the clock: the due date comes from the payment terms in Settings → Money & invoicing, and an unpaid invoice past it shows as overdue without anybody marking it. Partial appears as soon as a payment arrives that does not cover the balance; paid when it does.

Print gives the A4 document, Send emails it with a public link the client can open without a login, and the portal lists it for contacts who have access.

Recurring invoices

An invoice can be marked recurring with a period. Press Issue due invoices on the invoice list and every recurring parent that has come due produces its next copy as a draft.

A button rather than a scheduler, and safe to press twice. There is no cron on shared hosting, so recurring invoices wait for somebody to press the button — and each parent only advances one period per press, so pressing it three times on the first of the month does not bill a client three times.

Payments

Sales → Payments. A payment records money received against an invoice: the amount, the date, a method and a reference.

Part payments are ordinary — record each as it arrives, and the invoice moves to partial and then paid on its own. The payments list is the answer to "what came in this month", filterable by method and date, and the aged debt table in the sales report is built from what is still outstanding.

Nexus does not take card payments. There is no gateway and no checkout: a payment row is a record that money arrived in your bank, entered by whoever reconciles the account. See Deliberately not here.

Contracts

Sales → Contracts. A service agreement, maintenance, subscription, NDA or anything else: a client, a type, a value, a start and end date, and the body of the agreement.

Draft → sent → signed, and expired or terminated at the end. Send emails the client a link to the contract as a web page they can read and sign — a typed name and a timestamp, recorded with their IP address. Print gives the paper version with your letterhead.

The signature is a record of agreement, not a qualified electronic signature under any particular jurisdiction's law. For contracts that need one, use a signing service and attach the executed PDF to the record here.

Tickets & SLA

Sales → Support Tickets. A numbered ticket per issue: a subject, a category, a priority, a contact, an assignee and a status.

Tickets run open → pending → resolved → closed. Pending means waiting on the client — and a client reply on a pending ticket reopens it automatically, which is exactly the behaviour that stops things going quiet and getting forgotten.

The SLA clock

Every ticket gets a first-response target from its priority. The defaults are set in CRM Settings → Tickets & SLA:

PriorityFirst response due within
Urgent2 hours
High8 hours
Medium24 hours
Low72 hours

The first staff reply stamps the response time. A ticket past its due time with no reply carries a breached badge, and the breach rate is on the dashboard and in the reports.

Replying

A reply is either public — emailed to the client, visible in their portal — or an internal note, which stays inside the team. Canned replies are your email templates, offered on the reply box already merged for this ticket's contact, so picking one drops finished text in rather than something you still have to edit.

Either side replying raises the bell for the other. A client reply on a pending ticket reopens it and notifies the assignee; the first staff reply stamps the response time against the SLA.

Tickets can be created here, by a client in the portal, or over the API — which is how you point a support mailbox or a contact form at it.

Knowledge base

Sales → Knowledge Base. Articles with a category, a title, a slug and a body. An article marked public is readable at /kb/{slug} without a login, so you can link one from an email or a ticket reply; the rest stay internal. Each article counts its views, which over a few months tells you what your customers actually keep asking.

Clients reach the knowledge base from the portal, and agents from the ticket screen — which is where an article most often saves a reply.

Client portal

A separate front door at /portal for your clients. It is not the back office with things hidden: it is a different set of screens on a different login, and staff never use it.

ScreenThe client can
HomeSee what is outstanding: unpaid invoices with the amount due and how much of it is overdue, quotes and contracts awaiting a decision, open tickets, and the projects being run for them.
QuotesRead a quote and accept or decline it.
InvoicesRead and print invoices, and see what has been paid.
ContractsRead a contract and sign it.
TicketsRaise a ticket, read the thread and reply. Internal notes are never shown.
ProfileUpdate their own details and change their password.

Give a contact access from their record in CRM → Contacts — set a password and they can sign in. Turn the whole portal off in CRM Settings → Client portal; every portal address then returns a 404 rather than a login form.

Sign-in is rate limited to five attempts a minute per email and address, and a client only ever sees documents belonging to their own company.

Projects

Projects → Projects. A project has a name, a code, a client company and contact, a category, an owner, a period, a status, a priority, a budget and a colour.

Two fields decide how it bills: billable, and the hourly rate that logged time is charged at. A member can carry their own rate, which overrides the project's — see Billing a project.

Statuses are planning, active, on hold, completed and archived. Progress is a percentage Nexus keeps up to date: the share of the project's tasks that are done. Nobody types it.

Members and access

The Members tab is who works on it, each as a viewer, a member or a manager. This is the one place in Nexus where access is per-record rather than per-section:

WhoReaches
Anybody with the projects sectionEvery project, and the portfolio screens: all tasks, time tracking, workload, templates.
A project viewerThat project, read-only.
A project memberThat project: works tasks, logs time, comments, uploads.
A project manager, or the ownerThat project, plus running it: members, settings, milestones, sprints, billing.

So a developer with no HR or CRM access and no projects permission still opens the two projects they were added to, and nothing else.

Tasks

Projects → All Tasks for everything, or the Tasks tab inside a project.

A task carries a title, a description, a status, a priority, assignees, a start and due date, an estimate in hours, labels and a milestone. Priorities are fixed — low, medium, high, urgent — because every screen colours them. Everything else you define in Settings → Project Settings: the statuses that become board columns, the labels, and the project categories.

Structure

FeatureWhat it does
SubtasksA checklist inside a task, for work not worth its own card.
DependenciesTask B is blocked by task A. Trying to finish B while A is open is refused, naming the blocker — the rule is enforced, not advisory.
RecurringRepeat every n days, weeks or months, with an optional number of repeats. Finishing the task spawns the next occurrence with its dates shifted.
TimeEntries logged against the task, totalled against its estimate.
Comments & filesThe discussion and the attachments. Commenting notifies everybody on the task.

Assigning a task notifies the assignee. Every status change is logged, and finishing a task refreshes the project's progress figure.

Board, gantt & calendar

The same tasks, four ways. Each is a tab on the project.

ViewBest for
ListFiltering and bulk work — by status, assignee, priority, milestone, label or due date.
BoardDaily flow. Columns are your task statuses; drag a card to move it, and the order within a column is kept.
GanttDates and dependencies across the whole project — where the plan is stacked and where it is empty.
CalendarWhat is due when, month by month.

A task with no dates does not appear on the gantt or the calendar; that is usually the explanation when something seems missing from them.

Milestones & sprints

Milestones are dated markers within a project — a delivery, a sign-off, a release. Tasks point at one, so a milestone shows how much of what it depends on is done, and it appears on the gantt and the shared calendar.

Sprints are a period with a set of tasks and a goal. The sprint page draws a burndown: the estimated hours still open at the end of each day, against the straight line the team would follow finishing at an even pace. Days that have not happened yet are left blank rather than drawn flat, so the chart stops at today and does not imply a plateau that has not occurred.

Issues, notes & files

Three more tabs on a project, for the things that are not tasks:

TabWhat it holds
IssuesBugs and problems, with a severity, a status, a reporter and an assignee, plus fields for the steps to reproduce and the environment. Kept apart from planned work so a bug list does not distort the board — and an issue can point at the task that fixes it.
DiscussionsThreads about the project as a whole, rather than about one task.
NotesDocuments that live with the project — a brief, a decision record, meeting notes.
FilesEvery attachment on the project and its tasks, in one list.
ExpensesCosts booked to the project, marked billable or not. Billable ones can be pulled into an invoice.

Time tracking

Projects → Time Tracking, or the timer on any task.

Two ways to log time: press Start on a task and stop it when you are done, or type an entry with a date, a duration and a note. One timer runs per person, so starting a second stops the first.

Stopping a timer rounds the duration up to the block set in Settings → Project Settings — with the default fifteen minutes, seven minutes becomes fifteen and sixteen becomes thirty. The rounded figure is what gets stored, so the entry matches what gets invoiced.

Billable and approved

Entries are billable or not; the default for new entries is a setting. If time approval required is on, entries must be approved before they can be invoiced — the list has per-row approve and reject, and an Approve all for a filtered selection.

Export writes the filtered list as CSV, which is what most people actually want when a client asks for a breakdown.

Workload

Projects → Workload. Estimated hours of open work due per person per week, against what their week can actually hold — hours per day times the working days, holidays excluded.

An estimate on a task shared by several assignees is split between them rather than counted twice. Over-capacity weeks are coloured, which makes this the screen to open before promising a date.

Only tasks with an estimate and a due date can be planned, so a blank-looking workload usually means the estimates are missing, not that the team is free.

Project templates

Projects → Templates. A reusable project shape: milestones and tasks with their dates expressed as offsets — day 0, day 5, day 30 — rather than as fixed dates.

Creating a project from a template asks for a start date and applies the offsets from it. Whoever creates it becomes the owner. For an agency that runs the same onboarding on every client, this is the difference between a day of setup and a minute.

Billing a project

The one place the Projects module touches Sales. On a project, Invoice time collects every billable, approved, not-yet-billed time entry and every unbilled billable expense, and creates a draft invoice for the project's client.

Before you commit to it, the screen previews what would be billed: the hours, what they come to, the expenses, and the total.

On the invoiceHow it is built
Time linesOne line per person per task, so the client sees who did what. The quantity is the hours; the price is that person's rate.
The rateThe rate on that person's project membership if they have one, otherwise the project's hourly rate.
Expense linesOne line each, at the amount spent, untaxed.
Tax and termsTax at the default percentage from Money & invoicing; the due date from the payment terms there.

The entries and expenses are stamped as billed against that invoice, so the same hour is never invoiced twice. The invoice is a draft: check it, edit the lines, then send it like any other.

If time approval required is off, entries are approved as they are logged and are billable straight away. If it is on, nothing reaches an invoice until somebody approves it — which is the point of turning it on.

A project with no client company cannot be invoiced — there is nobody to bill. Set the client on the project first.

Dashboard

The dashboard is assembled from what you are allowed to see. A sales user gets pipeline and revenue; an HR user gets headcount, attendance and who is off today; a project manager gets delivery. Somebody with all three gets all three, in that order. Nothing is computed for a section you cannot open, so a narrow account is a fast page.

WidgetShows
MineEverybody gets this one: today's clock state, leave balance, tasks due, and what is waiting on you to approve.
PeopleHeadcount and its change, joiners and leavers per month, probations and contracts ending in the next 30 days, documents about to expire; present today and late today, submitted timesheets, worked and overtime trends; pending leave and who is off today; payroll net and gross per month, the last run, pending expense claims; open jobs, the candidate funnel, and today's interviews.
CommercialOpen pipeline with its weighted value and its shape by stage, open leads, activities due today and overdue, deals won and lost per month; invoiced against received drawn on one money axis — never two — so the gap reads as the collection lag it is; outstanding and overdue balances with the five worst invoices; ticket volume and SLA breaches.
DeliveryActive projects, tasks created and completed per week, overdue tasks and what is due this week, hours logged this week against last, unbilled hours waiting to be invoiced, open issues by severity, and the milestones coming up.

Headline figures show their change against the previous period, because a number without a direction is decoration.

Reports

Three report screens, each a set of tables with charts over them, and each table downloadable as CSV from the button beside it. Same numbers on screen, in the chart and in the file.

ReportTables
HRHeadcount by department, employment type and gender, with the number on probation and the average tenure; joiners, leavers and month-end headcount per month; attendance per month — present, late, half days, absent, overtime hours and late hours; leave days by type and by department; payroll gross, deductions and net per month; the recruitment funnel by stage, candidates by source, open jobs and average days to hire; expense claims by category and by status.
SalesLeads by status; deals by stage with weighted value; the conversion rate; won and lost per month; revenue per month; receivables aged into not-yet-due, 0–30, 31–60, 61–90 and 90+ days; top customers by invoiced and paid; performance by owner; ticket volume and SLA; campaign results.
ProjectsProjects by status; tasks completed per week and the on-time rate; hours logged by project and by person; billable against non-billable; estimate against actual with the variance; the workload grid; sprint velocity; issues open and resolved by severity.

Every report takes a period — a preset or your own date range — and the filters you would expect. All three need the reports section.

Activity log

Reports → Activity Log. Every state change in the system: who, what, when, from which address, and against which record.

Leave decided, a deal won, an invoice sent, a payslip paid, a setting changed, a backup taken, a failed email — all of it. Filter by person, by action or by date, and open the record from the row.

This is where to look when somebody asks who changed something, and where a failed notification email is recorded — the mail is wrapped so that a mail server being down can never stop a leave request being filed, and the failure lands here instead of on the screen.

Notifications & email

Settings → Notifications. Two things on one screen: how mail leaves the building, and which events raise one.

Mail

Choose SMTP, sendmail or log, set the host, port, username, password and encryption, and the from address and name. Send test email proves it before you trust it. Saving stores the values with this workspace and re-binds mail immediately — no restart, no cache clear. Leaving the password blank means "leave it alone", not "clear it".

The log mailer writes messages to storage/logs instead of sending them, which is the right setting on a staging copy that must not email real clients.

In the SaaS edition the server is per workspace. The details on this screen are saved in the workspace's own settings (the password encrypted), not in .env, so one company's SMTP never touches another's. The extra choice at the top, Platform default, means "send through whatever the platform operator set up" — the right answer for most customers, and what a new workspace starts on. Pick SMTP only to send from your own domain through your own provider.

Events

Ten events exist. Each raises the bell always, and sends an email only if its switch is on:

EventGoes toWhen
Leave requestedThe approverSomebody files a leave request.
Leave approved or declinedThe person who askedA manager decides it.
Payslip readyEach employee on the runA pay run is finalised.
Expense claim decidedThe claimantA claim is approved or declined.
Task assignedThe assigneeA task is assigned to somebody.
Task commentEveryone on the taskSomebody comments on it.
Lead or deal assignedThe ownerA lead or deal is handed over.
Invoice or quote sentThe client contactSomebody presses Send.
Ticket replyThe client, or the agentEither side replies.
Interview scheduledInterviewers and candidateAn interview is booked.

An optional admin copy address receives a copy of everything, which is useful while you are setting the system up and noise afterwards.

REST API

For a mobile clock-in app, a website that files leads, a status board of your own, or a support mailbox that raises tickets.

Tokens

Settings → API Tokens. Create a token, choose which member of staff it acts as, and optionally limit it to particular sections. The token is shown once — only a hash is stored, and a token list that can show you the secret is a token list that leaks every secret.

A token can never do more than its owner. Take a permission away from somebody, or deactivate them, and their integrations lose it at the same moment.

Calling it

curl https://acme.yourplatform.com/api/v1/ping \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"

Every response has the same shape — the payload under data, paging under meta — so a client never has to guess where to look. List endpoints take ?per_page= (clamped to 200) and ?page=.

{
  "data": [ { "id": 4, "name": "Amira Haddad", "employee_code": "EMP-0004" } ],
  "meta": { "page": 1, "per_page": 50, "total": 12, "last_page": 1 }
}

Endpoints

MethodPathNeedsWhat it does
GET/api/v1/pingany tokenConfirms the token works and reports what it may reach.
GET/api/v1/employeesemployeesThe directory, searchable and filterable by department and location.
GET/api/v1/employees/{id}employeesOne person in full.
GET/api/v1/attendanceattendanceAttendance rows for a date range.
GET/api/v1/holidaysattendanceHolidays, optionally for one location.
POST/api/v1/attendance/clockany tokenClocks the token's owner in, out, or on and off break.
GET/api/v1/leave-requestsany tokenThe owner's own leave requests.
POST/api/v1/leave-requestsany tokenFiles a leave request as the owner, under the same rules as the web form.
GET/api/v1/leadscrmLeads.
POST/api/v1/leadscrmCreates a lead — the endpoint for your own website form.
GET/api/v1/dealscrmDeals with their stage and value.
POST/api/v1/dealscrmCreates a deal.
PATCH/api/v1/deals/{id}/stagecrmMoves a deal to another stage.
GET/api/v1/contactscrmContacts.
POST/api/v1/contactscrmCreates a contact.
GET/api/v1/companiescrmCompanies.
GET/api/v1/invoicessalesInvoices with their status and balance.
GET/api/v1/invoices/{id}salesOne invoice with its lines and payments.
POST/api/v1/ticketssupportRaises a ticket — point a mailbox or a contact form here.
GET/api/v1/projectsprojectsProjects with their status and progress.
GET/api/v1/projects/{id}projectsOne project.
GET/api/v1/projects/{id}/tasksprojectsIts tasks.
POST/api/v1/tasksprojectsCreates a task.
PATCH/api/v1/tasks/{id}projectsUpdates a task — status, assignees, dates.
GET/api/v1/time-entriesprojectsTime entries for a range.
POST/api/v1/time-entriesprojectsLogs time.

Clocking and leave are self-service over the API too. Those three endpoints need no section — any valid token may call them, always acting as the token's owner. That is what makes a small mobile clock-in app possible without handing it the employee directory.

Rate limits and errors

120 requests a minute per token by default (Settings → General Settings → API), counted per token rather than per address, so several integrations behind one office connection do not throttle each other.

StatusMeans
401Missing token, unknown token, or the owner is deactivated. The message is deliberately the same for all three — a distinct "no such token" turns the endpoint into an oracle.
403The token may not reach that section.
404No such record, or one the owner may not see.
422The payload was rejected; errors names the field.
429Rate limited.

Every API call runs as a real member of staff, so it appears in the activity log under their name with the token noted.

Languages

Settings → Languages. Eight languages ship complete: English, French, Spanish, German, Portuguese, Turkish, Hindi and Arabic. Arabic switches the whole interface to right-to-left — layout, icons, tables and charts.

Tick which languages appear in the switcher, and pick the default for people who have not chosen. The default is always kept in the active list, so the switcher can never offer a language the site will not serve.

Translating

Nexus ships no English language file: every string in the source is its own key, so the untranslated product is English by construction and a missing translation degrades to readable English rather than to messages.save.

That means you never need a template. Open a language and the screen lists every string in the product with its translation beside it — filter to the untranslated ones and type. Four buttons help:

ButtonDoes
DownloadExports the locale as a JSON file — hand it to a translator.
UploadTakes the file back, merging it in.
PruneRemoves translations for strings that no longer exist in the product.
EnablePuts the language in the switcher.

Files live in lang/, one JSON per locale. To add a language beyond the eight, add it to config/languages.php with its native name and direction; it then appears here to be translated.

Data you type — department names, leave types, pipeline stages, ticket categories — is not translated. It is your content, and it shows in whatever language you entered it.

Workspace backups

Settings → Backup & Update, inside a workspace. Take a backup writes every table of that workspace's database to a timestamped .sql file, and the list below lets the workspace administrator download or delete their own dumps. It sees nothing of the platform or of any other workspace.

The dump is written in PHP rather than shelled out to mysqldump, so it works on any host. The output is plain SQL any MySQL client will restore, so a customer's data is never hostage to this product — they can take their database and leave, which is the right property for a SaaS to have.

The same screen shows the version the platform is running. The update instructions on it are Nexus's own and do not apply to a workspace: updates are done once, for everyone, by the operator — see Updating.

The backup covers the database, not the uploads. Logos, avatars, CVs, contracts and every file attached to a record live in public/uploads/t/{workspace}/ on the server, where only the operator can reach them. A customer who wants a complete export needs that folder from you.

For the operator's own backups of everything at once, see Platform backups.

Updating

An update is a file copy and one visit to Platform Update. The screen migrates the central database and every workspace in turn, behind a maintenance page, and releases it when done. In full:

  1. Back up

    Administration → Backups → Create backup, and copy public/uploads/ and .env somewhere safe.

  2. Copy the new files over

    Overwrite everything except .env, public/uploads/ and storage/.

  3. Run the updater

    Settings → General → Updates → Start update, and keep the tab open until every target reads done.

  4. Check the logs

    A workspace that failed to migrate is in Logs & System Health with the error. Fix the cause and re-run; targets already migrated are skipped.

With shell access, php artisan migrate --force && php artisan tenants:migrate && php artisan optimize:clear does the same without the maintenance page. The version you are running is in version.txt and on every workspace's Backup & Update screen.

Artisan commands

Everything the panel does has a command-line equivalent, plus a few things only the command line does.

CommandDoes
php artisan migrate --forceMigrates the central database only.
php artisan tenants:migrateMigrates every provisioned workspace database. --tenants=ID for one; the id is on the workspace page.
php artisan tenants:seed --class=DemoSeeder --tenants=IDFills one workspace with the twelve-person demo company — the HR, CRM and project sample data. For a demo, never for a customer.
php artisan saas:demo --subdomain=demoProvisions a workspace exactly as registration does, then seeds the demo company into it. Options: --name, --email, --password, --plan=slug.
php artisan nx:backupDumps the central and every workspace database to storage/app/backups. --tenant=subdomain for one.
php artisan db:seed --class=PlanSeederRestores the three launch plans (idempotent — matches on slug and updates).
php artisan db:seed --class=LandingCmsSeederWrites the marketing site's starting copy, only if none exists.
php artisan optimize:clearClears every cache. The same as the panel's Clear cache button.
php artisan testThe test suite, on in-memory SQLite.

Troubleshooting

The platform

SymptomCause and cure
Every page redirects to /setupstorage/app/installed is missing. Either the install did not finish, or the file was deleted. Re-run the wizard, or recreate the file if the database is already good.
/setup loads on a live siteThe same file is missing. Recreate it — until you do, anybody can walk the installer and wipe the central database.
A new workspace's address does not resolveDNS. The wildcard record is missing or has not propagated. The platform believes the workspace is ready because it is.
The subdomain resolves but shows the marketing site or a 404The web server is not passing the subdomain to this vhost — no ServerAlias *.yourplatform.com / server_name *.yourplatform.com. A genuine 404 with the platform's styling means the subdomain is not in the domains table: check the spelling against the workspace page.
The browser refuses the certificate on a subdomainThe certificate covers the apex only. Issue a wildcard.
Provisioning fails with Access denied or CREATE command deniedThe database user cannot create databases. Grant it, or set hosting mode to shared and create the database by hand, then Re-provision.
Provisioning hangs at Preparing…It runs after the response in the same PHP process; on a very slow server it can take a couple of minutes. If it never finishes and nothing is in the logs, the process was killed — check max_execution_time (the job raises it, but some hosts forbid that) and storage/logs/laravel.log.
Payment completes at the gateway but the workspace stays expiredNeither the return nor the webhook confirmed it. Check Transactions for a pending row, then the logs for a signature failure — a webhook secret from test mode on a live key is the usual cause. Fix the secret; then either have the customer revisit the return URL or record the payment by hand.
A gateway does not appear on the billing pageIt is not active, or a required credential is blank. Both are on Payment Gateways.
Everything answers 503 The platform is being updatedThe maintenance lock is on. Open Settings → General → Updates and either re-run or release it. From the shell: delete storage/app/platform-update.lock.
500 on every page after an updateStale caches. Delete the files in bootstrap/cache/ and run php artisan optimize:clear. If it persists, check storage/logs/.
500 on a workspace, fine on the platformLook in storage/logs/laravel.log for a cache-tagging error — the tenant cache store must support tags, and the database and file stores do not. The shipped code never caches in tenant context; a customisation that does will produce this.
Backup finished with errorsmysqldump was not found or could not reach a database. Set MYSQLDUMP_PATH; the failing database is named in the logs.
A platform admin cannot see a sectionTheir account is an admin without that box ticked. Administration → Super Admins.

Inside a workspace

SymptomCause and cure
Your plan allows up to N employeesThe workspace is at its seat limit. Upgrade from Plan & Billing, or the operator raises the limit on the plan or moves the workspace to another.
This workspace's subscription has expiredPast the grace period. The administrator renews from the button on that page; the operator can also record a payment or extend the date.
This workspace is suspendedOnly the operator can lift it.
The page loads unstyledpublic/build/ did not get uploaded, or the domain is not pointed at public/. Check both.
Logo or avatar uploads failpublic/uploads/ is not writable, or PHP's upload_max_filesize is smaller than the file.
No emails arriveSettings → Notifications, press Send test email. Failures are recorded in the activity log with the reason. Check the event's switch is on, and that the mailer is not set to log. A workspace on Platform default sends through the platform's MAIL_* in .env — if that is log, nothing leaves the server until the operator sets a relay.
A menu item is missingThat account lacks the section. Check Settings → Users & Roles. Hiding the link and blocking the route are the same permission.
A leave request is refusedThe balance does not cover it, the notice is shorter than the type's minimum, or the type requires an attachment. The message says which.
A task will not move to doneIt is blocked by another task that is still open. The message names it.
Payroll figures look wrongAlmost always the working time settings, the holidays for that location, or a timesheet that was never approved. Regenerate the draft run after fixing the source.
The workload chart is emptyTasks need both an estimate and a due date to be planned. Missing estimates read as free capacity.
Recurring invoices did not go outNothing is scheduled. Press Issue due invoices on the invoice list.
The portal returns 404It is switched off in CRM Settings → Client portal, or that contact has no portal access.

When something goes wrong on the server, storage/logs/laravel.log has the detail and Logs & System Health has the summary. Turn APP_DEBUG on only long enough to read the error, and turn it off again — it shows stack traces to everybody, including every workspace's users.

Deliberately not here

Every link in the sidebar opens a real screen. A few things a platform like this could plausibly do are absent on purpose, or stored but not yet delivered, and this guide would rather name them than let you find out:

Changelog

1.0.0

First release of the SaaS edition.

Credits & support

Nexus SaaS is built on Laravel with stancl/tenancy for the multi-tenant foundation, the Tabler interface, Alpine.js for the interactive pieces, Chart.js for the charts, Tabler Icons and Vite for the build, and the official Stripe SDK. Thanks to everyone who maintains them.

Getting help

When you write in, four things turn a slow answer into a fast one: the version from version.txt and the runtime line on Logs & System Health, the exact steps that produced the problem, whether it happened on the platform domain or a workspace subdomain (and which), and the last few lines of storage/logs/laravel.log. A screenshot of the screen you were on helps more than a description of it.

Nexus SaaS documentation · version 1.0
Thank you for buying it.