Arrivo — Visitor Management System

A self-hosted PHP/MySQL app to register, badge, track and check out visitors, pre-register guests, screen a watchlist, notify hosts, and keep a live on-site roster with a full audit trail.

1. Introduction

Arrivo replaces the paper visitor logbook at reception. It suits offices, factories, schools, clinics, gyms and gated communities. Key features:

2. Server requirements

Nginx

Apache works out of the box with the included .htaccess. On Nginx, add this to your server block (adjust the PHP-FPM socket):

root /var/www/arrivo;        # the folder that contains index.php
index index.php;

location / {
    try_files $uri $uri/ /index.php?$query_string;
}
# Never serve internal files
location ~ ^/(config|core|storage|install/sql)/ { deny all; }
location ~ \.(sql|md|log)$ { deny all; }
# Uploaded files are data only
location ^~ /uploads/ { location ~ \.php$ { deny all; } }

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

3. Installation

  1. Upload the package to your web root (or a subfolder) and unzip.
  2. Visit the site in a browser — you are redirected to /install.
  3. Step 1 — Requirements: confirm every row is green.
  4. Step 2 — Database: enter DB host, name, user, password. On shared hosting create the database in cPanel first.
  5. Step 3 — Account: organization name, admin name/email/password, timezone. Optionally tick demo data.
  6. Step 4 — Install: creates tables, your admin account, and config/config.php.
  7. Step 5 — Done: delete the /install folder, then sign in.
  8. Step 6 — Scheduled tasks (recommended): open System → Scheduled tasks and add the line shown there to your server's cron (cPanel → Cron Jobs), every 5 minutes. Without it, tasks still run while people use the app.
Manual install: copy config/config.sample.php to config/config.php, edit the credentials, then import install/sql/schema.sql into your database.

4. First-run setup

Configure your master data in this order so the check-in form is ready:

  1. Sites — your buildings/branches (every visit belongs to one).
  2. Departments — optional, can be scoped to a site.
  3. Visitor types — guest, contractor, delivery, interview… with their own rules (see below)
  4. Hosts — add them one by one, or import a spreadsheet (Administration → Import)
  5. Hosts — the people visitors come to see; optionally link one to a login.
  6. Users — add receptionists; admins manage everything.
  7. Settings — logo, agreement text, badge, retention, kiosk & public toggles.

5. Using the modules

Check-in (walk-in)

Front desk → Check-in. Enter the visitor (returning visitors are matched by phone/email and reused), choose host/site/department/visitor type, capture a photo (webcam or upload) and signature, accept the agreement, then Check in. A reference code and badge number are generated automatically.

On-site roster & check-out

On-site Now lists everyone currently in the building, grouped by site, with an overstay flag. Check out a single visitor, or use Check out all. Visitors still on site past the configured auto-checkout time are closed automatically.

Pre-registration & invites

Pre-register an expected visitor and share the invite link so they can confirm details or check status. On arrival, click Check in to convert the pre-registration in one step. Expired pre-registrations become no-show automatically.

Badges

Open a visit and click Badge to open the printable badge — your logo, photo, host, site, validity and a scannable barcode of the reference. Use Print badge for the designed HTML badge (prints full-page) or Download PDF. Choose from 8 layouts and an accent colour in Settings → Design (Browse & choose with demo data previews each one).

Label printers. In Settings → Design → Badge paper, choose Brother QL 62 × 100 mm, Dymo LabelWriter 54 × 101 mm (99014) or Zebra 4 × 3 in to print a high-contrast black label (name, visitor type, company, host, date and QR code) sized exactly to the label, in both the print view and the PDF. Set the same label size and landscape orientation in the printer driver and turn off browser headers and footers.

Design & branding

Settings → Design holds everything visual: upload your logo (shown in the menu, badges and kiosk), pick the app theme colours (primary + secondary, with preset swatches), choose the badge template & colour, and set an optional kiosk background. Picking a theme colour recolours the whole app and badges instantly — no code editing.

Visitor records

Visitors is the reusable directory (returning visitors are matched by phone/email). Deleting a visitor moves them to Deleted, where an admin can Restore them or permanently delete the record and all their visit history. Deleted visitors are hidden from badges, reports and lookups.

Watchlist

Add entries matched on name, phone, email or company. When watchlist screening is on, a matching visitor is flagged at check-in; staff may override with a logged reason. At the kiosk, matches are referred to reception.

Evacuation, roll-call and emergency message

Evacuation (menu) lists everyone on site now, per site or all sites, with a printable PDF and a CSV.

Kiosk

Enable kiosk mode in settings, then open /kiosk/ on a reception tablet for unauthenticated self-check-in (welcome screen with a live clock and touch tiles, check-in, "I'm expected" and self check-out). If Kiosk has a badge printer is on, the confirmation screen offers Print badge (HTML, auto-opens the print dialog) and Download PDF; otherwise it shows the on-screen pass for reception to print from the roster.

Reports & exports

Reports filters the full visit history by date, site, host, visitor type, status and source, with CSV and PDF export of the current filter. The CSV also contains the host-approval result and one column per custom field.

Visitor types

Administration → Visitor Types. Each type (Guest, Contractor, Delivery, Interview…) can have: a badge label and colour printed instead of "VISITOR" (e.g. an orange CONTRACTOR label); a default visit length, pre-selected at check-in and used for overstay alerts; its own agreement (e.g. a safety briefing) instead of the general one; a required photo or ID in addition to the general settings; and host approval. Reception and kiosk forms switch to the type's rules as soon as the type is chosen.

Host approval

When a visitor of a type with Host must approve the arrival checks in (reception, kiosk or a pre-registration), the visit is marked Waiting for host and the host receives an email with a secure Approve or decline link (email must be set up; no sign-in needed). Hosts who can sign in also see the request on their dashboard and under Approvals (the menu shows a count). Reception can decide on the host's behalf from the visit page or Approvals; every decision is recorded with who made it. The On-site Now list and the visit page refresh themselves while a decision is pending, and the kiosk asks the visitor to take a seat. A declined visit stays on site, clearly marked, until reception checks the visitor out.

Custom fields

Administration → Custom Fields adds your own questions to check-in: short or long text, number, date, a list of choices or a yes/no checkbox. A field can be required, asked of all visitors or only one visitor type, and shown on the kiosk or only at reception. Answers appear on the visit page, in the CSV export and are kept with the visit. Deleting a field also deletes its answers; switch it to inactive to keep them.

Import from CSV

Administration → Import adds hosts/employees, departments or watchlist entries from a spreadsheet saved as CSV (comma or semicolon separated; Excel's "CSV UTF-8" works). Download the sample file to see the columns. After uploading you see a preview: rows ready to import, rows skipped because they already exist, rows with problems (with the line number), and any sites or departments that will be created. Nothing is saved until you click Import. Existing records are never changed.

Dashboard and notifications

The dashboard shows visitors waiting for approval, live counts compared with yesterday, who is arriving next and who is on site now (with late arrivals and overstays highlighted), the 14-day trend and on-site numbers per location. The bell in the top bar lists what needs attention: approvals, overstays, watchlist events today, passes ending soon and visitor documents that expire. In the menu, the Administration group can be folded away; the browser remembers your choice.

Visitor passes

Front desk → Visitor passes → New pass. A pass gives a contractor or regular visitor one QR code that works every day between two dates at one site. Choose an existing visitor or enter a new one, the site, host, visitor type and dates, and optionally email the pass. At the kiosk ("I'm expected") or at reception (On-site now → Scan QR) each scan checks the visitor in, or out if they are already on site. The visitor type's rules still apply (valid documents, host approval). Revoke a pass to stop its QR code at once; the pass page lists every visit made with it.

Visitor documents

Open a visitor (Front desk → Visitors) to keep documents with expiry dates: insurance, safety induction, work permit… with an optional PDF or photo. Files are private: they are stored in a folder that cannot be opened from the web and are shown only to signed-in staff. Turn on Valid documents required for a visitor type (e.g. Contractor): expired or missing documents are then flagged at reception, block that visitor's pass and send them from the kiosk to reception.

Lobby TV screen

Open /lobby/ on a screen in reception (it opens on a paired kiosk device or for signed-in staff; full-screen it with F11). It shows a welcome message, the time, today's expected guests, who has just arrived and on-site counts, and refreshes every minute. In Settings → Access & capture choose how names appear (first name + initial is the default, for privacy) and the welcome message.

Insights

Safety & reports → Insights shows, for the last 7 days to 12 months: number of visits, different and returning visitors, average visit length, visits over time, the mix of visitor types, a weekday × hour map of the busiest times, and the top hosts and companies.

Touchless check-in (visitor's own phone)

With Touchless check-in on (Settings → Access), open Locations and click the QR icon on a site to print its A4 poster. Visitors scan it with their phone camera (no app), fill in the same form as the kiosk (agreement, signature, photo, custom questions, visitor type rules and watchlist screening all apply) and get a phone badge with their QR code, host and time. They can tap I'm leaving — check out on the same page, or scan the badge at the kiosk or reception. Each network address can check in up to 15 times an hour.

Kiosk and phone languages

With more than one language installed and Language buttons on the kiosk on, the kiosk, phone check-in and public pages show language buttons. The choice applies to that visitor only and switches back to the default language after they finish or the kiosk goes idle. Staff pages always use the language in Settings → General.

Visitor data requests (GDPR)

On a visitor's profile, administrators can Export data (a ZIP with data.json — profile, visits with answers, documents, passes, watchlist matches — and the visitor's photos, signatures, ID images and documents) or Erase the visitor: personal details, files, documents and answers are deleted and passes revoked, while the anonymous visit rows stay so reports remain correct. Both are recorded in the audit log. A visitor who is on site must be checked out first.

Kiosk devices (lock)

With Kiosk only on paired devices on (Settings → Access & capture, on by default), the kiosk opens only on tablets you have paired; any other browser sees a locked screen. To pair a tablet: in Kiosk devices type a name (for example "Lobby iPad") and click Add device. A pairing code appears, valid once for 24 hours. Open /kiosk/ on the tablet and enter the code. The tablet stays paired (for over a year, or until its browser data is cleared). The list shows when each tablet was last used. Click the bin icon to lock a lost tablet out immediately, or the arrows icon to create a new code for a tablet that needs pairing again. Signed-in administrators and receptionists can always open the kiosk to test it.

QR check-in and check-out

Every badge carries a QR code of the visit reference, and every pre-registration has its own check-in QR code: it is shown on the visitor's confirmation page, on the pre-registration screen and in the invite email. At the kiosk the visitor taps Scan my invitation QR code (no email needed, because the code contains a secret invite key) or Scan my badge QR code to leave. At reception, Scan QR on the On-site Now screen checks a visitor in or out. A USB barcode scanner that reads QR codes works in the same box. The camera needs HTTPS (or localhost).

Two-factor authentication

Every user can turn on two-factor sign-in in My Account: scan the QR code with Google Authenticator, Microsoft Authenticator, 1Password or a similar app and enter the 6-digit code. From then on, a code is asked for after the password. If someone loses their phone, an administrator opens Users and clicks Reset two-factor (shield icon) on that user.

Kiosk check-out

At the kiosk a visitor checks out by scanning the QR code on their badge, phone badge, invitation or visitor pass. Badge QR codes carry a secret code, so a photo or a guessed reference is not enough. When the visitor types the reference instead, they must also enter the last 4 digits of their phone number or their email. After 5 wrong attempts the kiosk asks them to wait a few minutes or see reception. Reception can always check anyone out from the on-site list.

Installable app (phone, tablet, desktop)

Arrivo can be installed like an app. In Chrome or Edge a download icon appears in the top bar (or use the browser menu → Install app); on Android choose Add to Home screen, on iPhone/iPad tap Share → Add to Home Screen. The app opens full screen with your name and colour, and shows a small offline page when the connection drops. HTTPS is required (it works on localhost for testing).

App icon: Settings → Design → App icon: upload a square image (512×512 or larger). Without one, the icon is made from your logo, and without a logo the default icon is used. Changing it updates the browser tab, home-screen and installed-app icon.

Notifications (bell)

The bell shows what needs attention right now. Opening it does not mark anything as read: an alert is marked read when you click it, or use Mark all as read. The badge counts unread alerts only, and an alert becomes unread again when it changes (for example the customer replies again).

Reference numbers

Settings → General → Reference style: Numbered (e.g. 2026-000123, the default) or Random (e.g. 7KQ2-9XMD), which does not reveal how many records you have. It applies to new records only; both styles keep working everywhere.

Light and dark mode

The moon / sun button in the top bar switches between light and dark mode for that browser. Until someone chooses, the app follows the device's own setting.

6. Settings reference

Settings is split into tabs: General, Design, Access & capture, Agreement and Email.

SettingTabWhat it does
Organization nameGeneralShown across the app, badges and emails.
Retention period (days)GeneralPersonal data on older visits is anonymized. 0 = keep indefinitely.
Auto-checkout timeGeneralVisitors still on site past this time are checked out automatically.
Date format / timezone / rows per pageGeneralDisplay preferences.
LanguageGeneralInterface language. English and Spanish are included; see Customization to add one.
LogoDesignShown in the menu, on badges and the kiosk (upload or remove).
App theme colourDesignPrimary & secondary brand colours (with preset swatches). Recolours the whole app; off = monochrome.
Badge template & colourDesignPick one of 8 badge layouts and an accent colour; preview with demo data.
Kiosk backgroundDesignOptional image behind the kiosk welcome screen.
Agreement / NDA textAgreementShown at check-in and on the kiosk. Plus kiosk welcome & privacy notes.
Watchlist screeningAccessScreen visitors at check-in.
Require photo / signature / IDAccessForce capture at check-in.
Public pre-registrationAccessAllow self-service pre-registration without a login.
Kiosk self-check-in / printerAccessEnable the tablet flow at /kiosk/, and whether the kiosk can print badges.
Lobby TV screen / names / messageAccessTurn the lobby screen on or off, how names are shown on it, and its welcome message.
WhatsApp & webhookIntegrationsHost messages on WhatsApp and event webhooks (see above).
Kiosk only on paired devicesAccessLock the kiosk to tablets paired in Kiosk devices (recommended).
Host loginAccessLet hosts log in to pre-register their own visitors. When off, host accounts cannot sign in.
Notify host on arrivalAccessEmail the host when their visitor checks in (needs SMTP).
CAPTCHAAccessSpam protection on public forms: a built-in math question (default, works offline), Cloudflare Turnstile or Google reCAPTCHA v2 (add the site key and secret key). Optionally also on the sign-in page.
Email (SMTP)EmailHost/port/user/password/from + a "send test email" button. Off by default.

System (Administration → System)

TabWhat it does
HealthChecks PHP version and extensions, memory and upload limits, disk space, HTTPS, environment, email, cron, database size and schema version, and that every upload and storage folder is writable. Copy system info copies a plain-text summary to paste into a support request.
Scheduled tasksOne cron job runs every task when it is due: visit clean-up (auto check-out and no-shows) and email retries (every 5 minutes), host approval reminders (every minute), and the data retention purge, daily backup and temporary file clean-up (daily). Use the command-line line (php /path/cron.php) or, if your host has no PHP command line, the URL line with its secret key. Run all now runs every task at once. If no cron job is set up, due tasks run after normal page requests (at most every 5 minutes).
Error logShows the last 200 lines of storage/error.log, newest first, with download and clear. Errors are written here when APP_ENV is production.
BackupsBack up now saves the database as a compressed .sql.gz (pure PHP, no mysqldump needed) and, optionally, the uploads folder as a ZIP, in storage/backups (blocked from the web). Download either part, delete, or Restore… (type RESTORE to confirm; a safety copy of the current database is made first). Upload and restore accepts a .sql.gz from another Arrivo server. Automatic daily backups run with the scheduled tasks; choose how many to keep. A backup is also made automatically before every database update.
Email logEvery email the app sends, with status and error. Failed emails are retried automatically (up to 3 attempts); Resend sends one again and the eye icon previews it. Entries are kept for 90 days.
MaintenanceShows a “back soon” page (HTTP 503) to everyone except administrators, with an optional message. It turns on automatically while a database update runs.

Privacy & security (Settings)

SettingWhat it does
Visitor personal data (days)Personal data on older visits is anonymized daily (default 90; 0 = keep). Visit counts stay for reports.
Audit log (days)Older audit entries are deleted daily (default 365; 0 = keep).
Sign out after inactivityMinutes of inactivity before a session ends (default 120).
Minimum password length / letters and numbersApplied to new users, password changes and resets.

After 5 wrong passwords an account is locked for 15 minutes; failed and locked sign-ins appear in the audit log. Each user can Sign out other devices from My account, and changing or resetting a password signs out every other session.

7. Roles & permissions

CapabilityAdminReceptionistHost*
Manage users / settings / master data / watchlist✅❌❌
Check visitors in / out, print badges✅✅❌
Pre-register visitors✅✅✅ (own)
Approve or decline arriving visitors✅ (any)✅ (on the host's behalf)✅ (own)
Visitor types, custom fields, import, settings✅❌❌
Visitor passes, documents, insights, lobby screen✅✅❌
View on-site roster / evacuation✅✅❌
DashboardAll visitorsAll visitorsOwn visitors only
Override watchlist hit✅✅ (logged)❌
View audit log / export reports✅Reports only❌

*Hosts sign in only while Host login is on (Settings → Access & capture). Link a host record to a user in Hosts / Employees.

8. Customization

9. FAQ & troubleshooting

Blank page after install

Ensure mod_rewrite is enabled and config/config.php exists. Set APP_ENV to development temporarily to see errors.

Mail not sending

Email is off by default. Enable it in Settings → Email, save, then use "Send test". Port 465 uses SMTPS, 587 uses STARTTLS. Use a real mailbox on your own domain as the sender. Hosts limit failed deliveries per hour: invalid host or visitor addresses cause bounces that can make the mail server refuse further mail for a while.

Upload or permission errors

Make /uploads and /storage, and every folder inside them, writable by the web server (for example chmod -R 775 uploads storage). The installer’s first step lists any folder that is not writable.

Installer says "already installed"

Delete config/config.php and install/install.lock to re-run (this does not drop your database).

Visitors are not checked out automatically at night

Auto check-out runs as a scheduled task. Set up the cron job from System → Scheduled tasks; without it the task only runs when someone uses the app.

Something went wrong — where do I look?

Open System → Health for anything marked “Problem”, then System → Error log. Use Copy system info when contacting support.

Camera doesn't work

Browsers only allow the webcam over HTTPS (or localhost). You can always upload a photo instead.

WhatsApp messages to hosts

Arrivo uses Meta's official WhatsApp Cloud API with your own WhatsApp Business number (Meta charges per message; there is a free tier).

  1. At developers.facebook.com create an app of type Business and add WhatsApp. Add and verify your business phone number.
  2. In WhatsApp Manager → Message templates, create two templates (category Utility, your language):
    visitor_arrived: "Hi {{1}}, your visitor {{2}} has arrived at {{3}}."
    visitor_approval: "Hi {{1}}, {{2}} has arrived and is waiting for your approval: {{3}}"
    Optional, for Evacuation → Emergency message: emergency_alert: "⚠ {{1}}"
    Wait until Meta approves them (usually minutes).
  3. Create a System user in Business settings with a permanent access token that has whatsapp_business_messaging permission.
  4. In Arrivo → Settings → Integrations enter the Phone number ID (WhatsApp → API setup), the access token, the two template names and their language code (e.g. en_US), switch on Send WhatsApp messages and click Send test.
  5. Give each host a mobile number in international format (e.g. +44 7700 900123) in Hosts.

Webhooks (Slack, Discord, Zapier, Make…)

Settings → Integrations → Webhook. Paste a URL and choose events (visitor checked in / out, host approval requested / decided). Arrivo sends a JSON POST with event, occurred_at, text/content (a ready-made sentence, shown directly by Slack and Discord incoming webhooks) and a visit object (reference, visitor, company, host, site, type, status, approval, times, link). The header X-Arrivo-Signature: sha256=<hmac> is the HMAC-SHA256 of the raw body with your signing secret, so your system can verify the request came from Arrivo. Notifications never delay or block a check-in; failures are logged in storage/error.log.

Updating to a new version

Make a backup in System → Backups and download it. Upload the new files over the old ones, keeping config/config.php and the uploads folder. Sign in as an administrator: if the database needs changes, a notice with an Upgrade now button appears at the top of the page.

Running a public demo

Add define('DEMO_MODE', true); to config/config.php. Everything stays editable, and the database and uploads are rebuilt from the demo data every DEMO_RESET_MINUTES (default 60). SMTP and CAPTCHA settings, password changes and the demo sign-in accounts are locked. Never turn this on for a real installation: each reset erases all data.

10. Changelog

1.0 — Initial release.

11. Credits & third-party licenses

Bundled open-source libraries (see /licenses): Bootstrap 5, Bootstrap Icons, jQuery, SignaturePad, Dompdf, PHPMailer, Chart.js, QR Code Generator, jsQR and the Inter typeface. All assets are bundled locally — the app needs no internet connection (only the optional Turnstile / reCAPTCHA providers load from their own servers).

12. Support

For help, contact us through your CodeCanyon item page. Please include your PHP/MySQL versions and any error message.