Egonix Cards — NFC business cards

NFC business cards: tap the card, the contact lands in a space where it can be exported, segmented and followed up.

Egonix Cards — NFC business cards
Role
CTO and co-founder
Stack
Laravel 13 · Livewire 3 · Filament 4
Product
NFC business cards
Status
Live

Context

A paper business card gets lost, and a contact made at a trade show almost never ends up in a usable list. Egonix replaces the card with an NFC card: tap it with a phone or scan its QR code, and the contact lands in a space where it can be exported, segmented and followed up.

Business challenge

Turn a contact made in person into a usable, exportable lead — without losing the paper card.

Solution

A single Laravel 13 application: the user interface in Livewire with MaryUI, the admin in Filament 4. Each card is identified by a UUID that serves both as its public URL and as the target of the capture API. Business rules live in services, permissions go through policies, and quotas depend on the Free, Premium or Enterprise plan.

Outcome

Live, in French and English: public profile with vCard and QR code, five-phase card editor, company space with invitations, roles and brand kit, e-mail campaigns, API keys and signed webhooks. Security, CI, Sentry and Playwright tests shipped with the March 2026 relaunch.

My role

CTO & co-founder

Architecture

Users

  • NFC card and QR code
  • Personal dashboard
  • Company space
  • Filament admin
  • Integrations (API, webhooks)

Application

  • Laravel 13 · Livewire 3 · MaryUI
  • Public profile /c/{uuid}
  • Capture API and API keys
  • Contact, billing and webhook services
  • Database queue
  • Policies and 2FA

External services

  • Stripe · Cashier
  • Google, Apple, LinkedIn OAuth
  • Sentry (personal data scrubbed)
  • MySQL (production)
  • Docker image on GHCR

The flow, step by step

  1. Contact

    Someone taps the card: the chip opens /c/{uuid}, which shows the profile, redirects to a URL or downloads a vCard depending on the card’s setting.

  2. Capture

    They leave their details in the form, or an integration calls the capture API with an API key.

  3. Validation and quota

    The request is validated, then the plan quota is checked; past it, the API answers 422.

  4. Storage

    The contact is created and attached to the card’s profile.

  5. Webhook

    The contact.created event goes out to the customer’s URLs, HMAC-signed, with three attempts.

  6. Follow-up

    The owner finds the contact, exports it as CSV or XLSX, or adds it to a campaign.

Data model

The main entities, their key fields and their relations.

User

  • role_id · society_id
  • Stripe columns
  • 2FA columns
  • language
  • 1—n Nfc
  • 1—n Contact
  • 1—n Profile

Nfc (card)

  • uuid (primary key)
  • profile_id · status_id
  • design (material, colours)
  • n—1 Profile
  • 1—n NfcDesignVersion

Profile

  • identity
  • theme_id · address_id
  • team_id
  • n—n Contact

Contact

  • email · phone
  • source · nfc_id
  • marketing_consent · locale
  • n—n Tag
  • n—1 Segment
  • 1—n Activity

Society (company)

  • owner_id
  • logo · theme
  • brand kit
  • n—n User (role)
  • 1—n SocietyInvitation

EmailCampaign

  • status
  • scheduled_at
  • segment
  • 1—n EmailCampaignRecipient

ApiKey

  • key_hash
  • last_used_at
  • revoked_at
  • n—1 User

OutgoingWebhook

  • encrypted secret
  • events
  • failure_count
  • 1—n WebhookDelivery

Full technical sheet

Languages
PHP 8.4, JavaScript, TypeScript (Playwright)
Framework
Laravel 13
Front end
Livewire 3, MaryUI 2, Tailwind 3, DaisyUI 4, Vite 8, Chart.js, in-house design system
Admin
Filament 4, 8 resources
Database
MySQL in production, SQLite in development and CI
Queues
Database driver: webhook delivery (3 attempts) and campaign sending
Authentication
Sessions, Google, Apple and LinkedIn OAuth, TOTP 2FA, mandatory e-mail verification, Sanctum
Payments
Stripe Checkout and Cashier 16, plan swaps, trials and promo codes
NFC
The chip holds the /c/{uuid} URL; generated QR code; vCard built server-side
API
OpenAPI spec linted by Spectral, Swagger docs off by default in production
Hosting
Multi-stage Docker image published to GHCR on every tag; Apache in production
CI/CD
GitHub Actions on a self-hosted runner: Pint, PHPUnit, build, scheduled E2E, Composer and npm audits
Tests
PHPUnit 12 (117 tests), Playwright (about 65 tests)
Observability
Sentry on server and browser, with personal data scrubbing
Languages (UI)
French and English, language stored per contact
Security
Security headers, SSRF guard on webhooks, hashed API keys, per-plan rate limits, CSV formula-injection protection, audit log

Key features

  • Card editor

    Templates, palette, free placement, undo and redo, material, version history.

  • Public profile

    vCard, redirect or profile, and QR code.

  • Contacts

    Guided CSV import with deduplication, CSV and XLSX export, tags and segments.

  • Company space

    Invitations, roles, locked brand kit, analytics and bulk orders.

  • Campaigns

    E-mails and sequences, with consent management.

  • Integrations

    API keys and signed outgoing webhooks.

Problems solved

  1. 500 error on the company space

    ProblemMySQL strict mode rejected a GROUP BY on an expression; SQLite doesn’t check it, so tests passed.

    FixGrouped by the alias, fixed in production the same day.

  2. Every icon broken in production

    ProblemApache’s global /icons/ alias intercepted the sprite requests.

    FixSprite moved to a folder that doesn’t clash.

  3. Forgeable Livewire requests

    ProblemThe design save accepted IDs changed in the browser.

    FixOwnership check and IDs locked server-side.

  4. Securing the API without breaking integrations

    ProblemExisting integrations called the capture API without a key.

    FixPhased roll-out, Deprecation and Link headers, and a configurable deadline.

  5. Vanished domain

    ProblemCards pointed to an old domain that no longer resolved.

    FixThe card domain is derived from APP_URL.

Technical choices

Choice Why
Queue, cache and session in the database No Redis to run, in development or in production.
UUID as the public identifier A card URL reveals neither how many cards exist nor their order.
Filament for the admin only The customer UI keeps its design system; the admin benefits from ready-made resources.
OpenAPI docs off in production A variable turns them on when needed.

Timeline

  1. April 2024 First commit, models and migrations
  2. March 2026 Relaunch: website, dashboards and NFC cards
  3. March 2026 Stripe, 2FA, webhooks, CI, Sentry and Playwright
  4. June 2026 Version 2.0.18
  5. August 2026 Design system v3 and redesign
  6. September 2026 Five-phase card editor

In numbers

160
commits, 145 of them mine
66
models
65
migrations
~180
tests (PHPUnit and Playwright)

Taken from the project’s source code and Git history on 8 October 2026. No usage or revenue figures are published.

Related projects

SaaS
Jan 2024 — present
Finnexus — Laravel Finance project
Enterprise finance SaaS with analytics, cash-flow forecasts and treasury insights.
Laravel, Livewire, TailwindCSS, Stripe
View project
SaaS
Oct 2024 — present
Vetzy — Laravel SaaS project
SaaS veterinary booking platform for clinics and pet owners with scheduling, notifications and client portal.
PHP, Laravel, Livewire, TailwindCSS, SaaS
View project
SaaS
Jan 2024 — present
StreamCrea — Creator dashboard
Twitch, YouTube and Kick sync, multi-network publishing and a subscriber blog.
PHP, Laravel, Livewire, Stripe
View project
SaaS
Jan 2022 — Dec 2022
MissionsCMS — Web App project
Mission management system with tracking dashboards and workflow tools.
Laravel, Livewire, Alpine.js, TailwindCSS
View project

Available for PHP / Laravel / WordPress / Silverstripe missions in Belgium and remote EU.