Download the PHP package goldnead/statamic-leadhub without Composer

On this page you can find all versions of the php package goldnead/statamic-leadhub. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package statamic-leadhub

LeadHub for Statamic

Turn Statamic form submissions into contacts, timelines, and follow-ups — directly inside your Control Panel.

tests Statamic 6 PHP 8.2+

LeadHub is a lightweight lead manager built into the Statamic Control Panel. Instead of treating every form submission as an isolated event, LeadHub automatically creates contacts, merges repeated inquiries by email, tracks a timeline of submissions and notes, and helps you follow up with the right leads at the right time.

It is not a full CRM. It's the missing layer between your website forms and your sales tools.


What you get

CRM-core modules (opt-in)

LeadHub can grow from a lead-capture layer into a lightweight CRM. These modules are off by default and require the eloquent driver — enable them under features in config/leadhub.php:

What it deliberately does not do (yet): bidirectional CRM pull sync. See the roadmap.


Requirements


Installation

Not on Packagist yet. goldnead/statamic-brand-context is still private, and Composer ignores the repositories block of a package it installs as a dependency. Until both packages are published, the command above resolves only in a project that declares the VCS repository itself:

That's it — no front-end build step is required. LeadHub ships its compiled Control Panel assets (Inertia + Vue 3 + Tailwind v4) under resources/dist/, and Statamic publishes them to your public/vendor/ automatically on install. If you ever need to (re)publish them manually:

Optional — publish the config to customize statuses, redaction rules, and feature flags:

After installation, you'll see a new LeadHub entry in the Control Panel sidebar.


Quick start

1. Connect your first form

  1. Open Control Panel → LeadHub → Forms
  2. Click Configure on the Statamic form you want to capture
  3. Toggle Enable LeadHub for this form
  4. Map the form's email field (required) and any other fields you want to capture
  5. Save

The next time someone submits the form, a contact appears in LeadHub → Contacts.

2. Work the leads

3. (Optional) Customize statuses

Edit config/leadhub.php:

The status list stays in the file — removing a status would strand every contact sitting on it, so it is shown read-only in the Control Panel. Which of them a new contact starts on, and most other keys of this file, are editable under LeadHub → Settings.


Permissions

LeadHub registers sixteen permissions under the LeadHub group. Assign them to roles in CP → Users → Roles.

Permission What it allows
view leadhub The dashboard, the deal board, the deal detail page, the companies, tasks and scoring screens (read-only), and the sync log. The read side of the CRM-core modules rides on this one permission rather than a view of its own.
view leadhub contacts The contact list, a contact's detail page and its timeline, and the follow-up list.
create leadhub contacts Creating a contact by hand (also needs features.manual_contacts).
edit leadhub contacts Editing a contact, adding a note, setting and completing a follow-up. Historically it also covered completing a task and moving a deal, and it still does — see below.
delete leadhub contacts Deleting a contact for good.
archive leadhub contacts Archiving and unarchiving a contact.
export leadhub contacts The CSV export of the contact list, filters included.
manage leadhub tags The Tags screen: creating, renaming and deleting tags.
view leadhub segments The Segments screen, read-only, including a segment's member list.
manage leadhub segments Creating, editing, activating and deleting segments, and the rule editor.
manage leadhub companies Creating, editing and deleting company records. Deliberately separate from the contact permissions: "may edit a contact" and "may delete the company behind fifty contacts" are not the same authority.
manage leadhub tasks Creating, editing, assigning, completing and deleting tasks.
manage leadhub opportunities Creating, editing and deleting deals, and moving one to another stage.
manage leadhub scoring Editing the per-brand point table under LeadHub → Scoring. Its own authority, because that table decides the score of every contact at once.
manage leadhub form mappings The Forms screen: enabling LeadHub per form and mapping its fields.
manage leadhub settings The Settings screen (see Settings in the Control Panel) and the pipeline-management screen — creating pipelines, adding, renaming, reordering and deleting stages.

Moving a deal accepts either permission

POST /pipelines/opportunities/{opportunity}/move — the board's drag & drop and the stage-change form on the deal page — is satisfied by either manage leadhub opportunities or edit leadhub contacts:

Until v2.4.0 the route asked for edit leadhub contacts alone, which matched neither of its neighbours: looking at the board is view leadhub, creating and deleting a deal is manage leadhub opportunities. Narrowing it to the correct permission alone would have taken drag & drop away, on upgrade day, from every install whose roles carry only the old one. So the route was widened instead: nobody loses a capability, and a role set up to run the pipeline gains the one it should have had. TaskController::complete() accepts the same pair, for the same reason, since v1.7.0.

If you are cutting a new role for the pipeline, grant manage leadhub opportunities. The fallback exists for the roles that already carry the old permission.


Configuration overview

Settings in the Control Panel

The config file is the default, not the last word. 28 of the keys above can also be changed in the Control Panel (permission: manage leadhub settings): the behaviour on a new submission, the payload redaction list, all thirteen feature flags, the export target and queue threshold, the scoring fallback values, the click-tracking dedupe window, and the notification switches.

The screen is the suite's shared settings screen in goldnead/statamic-brand-context, one section per addon that offers settings; LeadHub → Settings links to it, and the old URL /cp/leadhub/settings redirects there. LeadHub declares nothing but the field list (Support\Settings, implementing ProvidesSettings) — the store, the form, the validation and the config override belong to that package.

Only the difference to the config file is stored, one row per changed key in brand_settings, under the namespace leadhub. A value set back to what the file says deletes its row again, so config/leadhub.php stays the default and a later release can still move it. An install that never opens the screen behaves exactly as it did before the screen existed — including its queue workers, since the overrides are applied when the application boots rather than in a CP middleware.

Settings are per brand. They used to be per installation, in an un-branded leadhub_settings table, which meant two brands on one install shared a row. The upgrade migration puts every existing row on the default brand and leaves the old table in place for one minor version, so a rollback does not lose them.

If you edit the file on a server, check the screen: a stored override outranks the file, and a key changed in both places will not do what the file says.

Not editable there, on purpose:

On a flat-driver install, where migrations are not required, the brand_settings table may genuinely not exist. Reading survives that: no overrides means the config file, which is correct. Writing does not — run php artisan migrate if you want the screen to save.


Lead assignment & notifications

Assign an owner to any lead and keep your team in the loop by e-mail.

Switch them on and set recipients in config/leadhub.php (or via env):

notifications.enabled, recipients and digest.time are env-driven and stay in the file or the environment. The individual switches (new_lead, on_assignment, on_task_assignment, digest.enabled) are also editable under LeadHub → Settings.

The digest is wired into the Laravel scheduler automatically. Make sure your app runs the scheduler (php artisan schedule:work, or a cron entry calling schedule:run). You can also trigger it manually:

Assigning leads to different team members means more than one CP user, which requires Statamic Pro (STATAMIC_PRO_ENABLED=true).

Notifications use Laravel's mail channel, so they respect your existing MAIL_* config. Sending is fail-safe — a mailer error is logged and never blocks the lead pipeline.

Who they come from (multi-brand)

Since 2.1.0 every one of these mails leaves as the brand the contact belongs to, not as the host. The address, the sender name, the transport and the language come from brands.settings.mail through goldnead/statamic-brand-context 1.8+:

Why it matters even for internal mail: one relay account verifies one set of sending domains. Send brand A's alert through brand B's account and the provider rejects the address or silently rewrites it. Nobody outside sees the wrong name — what they see is a lead that was never followed up, because the alert never arrived.

Rebind Goldnead\Leadhub\Contracts\SenderIdentityResolver to answer differently for this addon alone; rebind the brand-context contract to answer for every addon that has not been rebound.


Marketing attribution

When features.attribution is on, LeadHub captures campaign context from the originating form submission and stores it on the contact:

Contact field Default submission source
utm_source utm_source
utm_medium utm_medium
utm_campaign utm_campaign
utm_term utm_term
utm_content utm_content
referrer referrer
landing_page landing_page

Capture works automatically as long as those values reach the submission — typically by adding hidden fields to your form populated from the query string / document.referrer. Remap any field name in config/leadhub.php:

The captured values appear in an Attribution panel on the contact detail page and are included in CRM payloads and exports.


Revenue per contact

LeadHub knew what a person did and never what they paid. It does now — as a ledger any contributor may write into, plus totals cached on the contact so a segment can compare them and a listing can sort by them.

Nothing here knows what a product is. An amount, a currency and a stable reference is the whole of the contract:

Three rules worth knowing before you contribute to it:

The totals (revenue_cent, revenue_refunded_cent, purchase_count, first_purchase_at, last_purchase_at) are a cache, recomputed from the ledger in a single statement after every write. When the two disagree the rows are right; RevenueService::recalculate($contact) repairs the cache. All five are available to segments, so "has paid more than 100 €" is a rule and not a report.

Eloquent driver only, like deals: the flat-file store has no table to aggregate over.

One page per person

The contact screen answers "what is going on with this person" in one place. Above the detail sit five numbers — first contact, last contact, purchases, lifetime value per currency, active access — and below it one timeline, newest first, that merges LeadHub's own events with what the sibling addons know:

Source What it adds Matched on
goldnead/statamic-payments purchases with their line items, pending and failed payments, refunds LOWER(TRIM(email))
goldnead/statamic-entitlements access granted / expired / revoked, with the state that is true now subject ('email', address) and (Contact, id)
goldnead/statamic-booking appointments, dated by the appointment LOWER(TRIM(email))
goldnead/statamic-consent consent decisions the consent_id in metadata_json or custom_fields — consent records carry no address, on purpose

Each reader lives inside LeadHub (src/Integrations/Timeline/), refers to its neighbour by a string class name and runs only when that addon is installed and migrated. Nothing is required; an install with LeadHub alone shows its own events and the revenue ledger's totals. A reader can be switched off under leadhub.timeline.sources, and leadhub.timeline.limit caps the merged list.

When the payments reader runs, the payments.* events that payments' bridge writes into leadhub_events are hidden, so a purchase appears once. When it does not run, they stay — they are then the only record. Payments are narrowed to the contact's brand plus brand_id = 0 (what a single-brand install wrote before brands existed); the shop itself is one pot.

A reader that throws while the screen renders is logged, left out and named on the screen ("Could not read source …"); the page never fails because a sibling is mid-upgrade.

Matching runs on LOWER(TRIM(email)) in the neighbour's database. SQLite lower-cases ASCII only: an address stored there with an upper-case umlaut (Bärbel.ÖZTÜRK@…) is not matched to a contact whose normalized address has it in lower case. MySQL and PostgreSQL fold Unicode and are not affected.

A host or another addon can contribute a feed of its own:

Grant access from the contact

With entitlements installed, the Actions panel gains Grant access: pick a product, add a note, and LeadHub writes through the entitlements facade (Entitlements::grant(), source manual, reference leadhub:<contact uuid>, the note and the granting user in meta). Idempotent per contact and product; a revoked grant stays revoked, as the facade guarantees. The product list is payments' catalogue when payments is installed (a bundle grants every slug it carries), otherwise the slugs entitlements has already seen. The click is also recorded on the LeadHub timeline as access_granted.

Its own permission — Grant access (entitlements), grant leadhub access — because reading a contact and opening a paid course for them are not the same authority. Without it the route answers 403; with it but without entitlements, 404.

CRM connectors & sync log

Push contacts to external systems when they're created, updated, or their status changes. Turn the feature on, then declare one or more destinations:

Built-in drivers

triggers controls which lifecycle events a destination listens for — any of created, updated, status_changed. Omit it to listen for all three.

Custom drivers. Register your own destination from a service provider:

Each destination implements Goldnead\Leadhub\Contracts\CrmDestination (driver(): string and push(Contact): SyncResult).

Sync log. Every attempt runs on the queue and is recorded twice: once on the contact's timeline, and once in a dedicated log surfaced under LeadHub → Sync log (contact, destination, event, status, HTTP code, message, timestamp). Failed jobs retry with backoff. On the flat-file driver the dedicated log table is skipped gracefully — the timeline entry is still written.

Syncs are queued, so configure a real queue worker (QUEUE_CONNECTIONsync) in production for non-blocking pushes.


Brands & multi-tenancy

Every LeadHub record belongs to a brand. Brands come from goldnead/statamic-brand-context, which is why that package is a hard require and not a suggestion: without it there is no tenant to scope a contact to.

What that means in practice:

Statamic sites

LeadHub has no notion of Statamic sites. A multi-site install does not get one contact pool per site out of the box — separation is done with brands instead. If your sites map onto tenants, model them as brands; if they do not, all sites share one pool.


Architecture

LeadHub ships with two storage drivers. Choose the one that fits your project:

eloquent (default)

Dedicated database tables. The core: leadhub_contacts, leadhub_events, leadhub_notes, leadhub_tags, leadhub_contact_tag, leadhub_followups, leadhub_form_mappings, leadhub_sync_logs. Control Panel settings live in brand_settings (owned by goldnead/statamic-brand-context); leadhub_settings still exists but is no longer read, and is dropped in a later release. The opt-in modules add leadhub_companies, leadhub_contact_company, leadhub_tasks, leadhub_pipelines, leadhub_stages, leadhub_opportunities, leadhub_stage_transitions, leadhub_scoring_rules, leadhub_segments and leadhub_segment_contact.

Five identifiers are unique per brand rather than globally: a contact's normalised email address, a tag slug, a pipeline slug, an event dedupe_key, a form mapping's form_handle and a segment handle. To check that the database is actually enforcing that — which is not the same question as whether the migrations ran:

It prints every colliding row it finds and never deletes one. See the 1.10.1 entry in the CHANGELOG for when you would need it.

flat (Statamic-native)

Stores leads as YAML files under content/leadhub/, with a Stache-style JSON index for fast lookups.

Switching drivers

You can move existing data between drivers without losing anything:

After switching, set LEADHUB_DRIVER=flat (or =eloquent) in your .env and clear caches.

If you ever edit the flat-file YAML by hand, rebuild the indexes:

The original Statamic form submissions remain untouched — LeadHub stores only references and a redacted payload copy, regardless of driver.

How a submission becomes a contact

The listener is fail-safe: any exception is caught and logged. A LeadHub error never breaks the original form submission flow.


Deals & stage history

Enable features.pipelines. Alongside the Kanban board, every opportunity has a page of its own:

Reading it is view leadhub, the same authority as the board it sits on. Every action offered on it is manage leadhub opportunities (or, for the stage change, either permission), and the permissions travel to the page as props, so it never draws a button that would answer 403.

The page carries four things:

The note is the only record of why

leadhub_stage_transitions has been written since the pipelines module shipped — one row per move, with the note — and until v2.4.0 nothing read it. The contact timeline records that a stage change happened and carries the ids; the note is not in it. The stage-change form on this page (and the board's drag & drop, which has no note field) is what writes that row, so the note is the only thing that will tell a later reader why the deal moved. Maximum 2000 characters, rendered ungrouped in the history.

Changing to the stage the deal is already on writes nothing. The page prevents it in the browser, a second tab or a bare POST does not, and a history entry reading "Proposal → Proposal" is exactly the noise that makes a history unreadable.

Time in stage

Each entry shows how long the deal sat there: the gap to the next entry, and for the newest entry the gap to now — or, on a closed deal, the gap to when it closed. A deal won in April would otherwise read "115 days" at the top and grow by one every day, in the same column and type as the real dwell times beneath it, answering a different question. Only a still-running stretch carries the "running" marker, and the footnote under the history says which of the two you are looking at.

A deal that was never moved has no transition row at all. Its first entry is built from opportunities.created_at and is a full entry, not a gap — otherwise the most common deal on a young install would show an empty panel, which reads as "nothing recorded" rather than "created here, still here".

Stage ids in the history carry no foreign key, so a stage that was emptied and then deleted leaves rows pointing at nothing. Those are labelled as removed rather than dropped. Stage names are resolved in one query for the whole history: a deal with 30 moves costs the same six queries as one with three.

Upgrading: won_at / lost_at

Before v2.4.0 StageTransitionService set these two timestamps and never cleared them again, while status, outcome and closed_at next to them were reset properly. A reopened deal therefore carried a won date while being open, and a deal moved from won straight to lost carried both. Nothing showed those columns, so nobody saw it — and won_at is precisely the column somebody groups revenue by.

The service now writes both stamps in both branches, the applicable one to now() and the other to null. The migration 2026_08_15_000001_repair_leadhub_opportunity_outcome_stamps cleans up the rows already stored: open deals lose both stamps, closed ones keep the one their outcome names. It parks the old values first, in metadata_json under repaired_outcome_stamps, so a report built on won_at can be reconciled after the fact. down() is deliberately empty.

If you report on won_at or lost_at, read that key before you conclude a number changed for a business reason.


Lead scoring

Enable features.scoring. Every scored activity adds points to the contact's engagement_score, which appears on the contact detail page and as a sortable, range-filterable column in the contact list. Each change writes a score_changed entry to the contact's timeline and fires LeadHubContactScoreChanged (available as the leadhub.score.changed webhook trigger).

Rules live in the database, per brand

The point table is edited in the Control Panel under LeadHub → Scoring (manage leadhub scoring), and it is scoped per brand: the same activity can be worth 50 points in one brand and 3 in another. A rule is an activity type plus its points; the special type * is the catch-all for everything without a rule of its own. A deactivated rule behaves exactly as an absent one and falls through to the catch-all.

Upgrading from a config-based point table

leadhub.scoring is still read as the fallback. While a brand has no rules, the config decides, exactly as before — updating the addon changes no score. This is the same "database over config" arrangement the settings screen uses everywhere else; what is specific to scoring is that the point table is per brand, while the settings apply to the whole installation. Note that scoring.default and scoring.timeline are among the values editable on that screen, so the fallback itself may be an override rather than the file.

Copy the config values into the table when you are ready:

The command is idempotent, and it never overwrites a rule whose points differ from the config file — a rule that differs is one somebody edited in the CP. Use --force to overwrite deliberately, --brand=<handle> to restrict it.

Changing a rule affects future activity only. Scores already awarded are a running total on the contact and are not recalculated.


Segments

Segments are dynamic groups of contacts defined by rules. Membership is materialized and kept up to date automatically: reactively when a contact changes, and via a daily sweep for time-based rules. Build them in the Control Panel under LeadHub → Segments with a live "matching contacts" preview.

Rule vocabulary

A segment's rules are a boolean tree of all / any groups (groups nest):

An empty rule set matches nobody — express "everyone" as no segment at all.

How membership stays fresh

Consumer contract (public facade)

segmentMemberIds() returns contact UUIDs and resolves live from the segment's rules (not the materialized pivot), so consumers always see the current set. It returns [] for an unknown or inactive segment. Guard optional integrations with method_exists(LeadHub::getFacadeRoot(), 'segmentMemberIds') so older LeadHub versions degrade gracefully.

Both storage drivers are supported: eloquent materializes membership in the leadhub_segment_contact pivot; flat mirrors segment handles onto each contact's YAML.


Webhooks & outbound integrations

LeadHub doesn't ship its own webhook-sending UI — instead it fires a complete set of plain Laravel events across the contact lifecycle. That makes it a first-class event source for any webhook addon, queue, or listener you already run.

The contact, follow-up and segment events extend LeadHubEvent and carry $contact, an optional $actor (the acting user, if any) and an optional $metadata array. The module events carry their own subject instead — $company, $task, $opportunity — with the same $actor and $metadata. LeadHubContactScoreChanged carries the contact plus $oldScore, $newScore, $delta and a $reason.

Pairing with goldnead/statamic-webhook-manager

goldnead/statamic-webhook-manager is an event-driven outbound-webhook addon: you pick a trigger in the CP, point it at a URL, and it handles payload templating, auth (HMAC / bearer / basic), retries, delivery logging and replay.

Install both addons and it just works — no glue code. When LeadHub boots and detects the webhook manager, it registers eighteen of the events above as webhook-manager triggers:

The company, opportunity and remaining task events are not bridged — their subject is not a contact, and the bridge hands the contact over as the payload. Listen for them directly with Event::listen(), as below.

Each fires a TriggerDetected event carrying the contact as the payload (plus actor, metadata and the event handle), so you create a webhook in Webhook Manager → Webhooks, choose e.g. "LeadHub — status changed" as the trigger, and you're done:

The bridge is wrapped fail-safe — a webhook-manager error is logged and never breaks the LeadHub pipeline. Opt out any time with 'features' => ['webhook_manager' => false] in config/leadhub.php. Under the hood it lives in src/Integrations/WebhookManager/ and only loads the addon's classes once they're present, so LeadHub never depends on the webhook manager.

If you don't run a separate webhook addon, LeadHub's built-in webhook CRM driver covers the common case directly — an HMAC-signed JSON POST on create / update / status change, with a Sync log. Use the webhook manager when you want CP-managed routing, templating and replay across many event types; use the built-in driver when you just need contacts pushed to a URL.

Rolling your own listener


Testing

LeadHub ships with Pest unit and feature tests:

The test suite uses orchestra/testbench with an in-memory SQLite database — no project setup required.

Code style and static analysis run from the same place:

PHPStan runs against phpstan-baseline.neon, which freezes what src/ already carries. It is a ratchet for new code: shrink the baseline when you touch a file, never grow it.

The MySQL run

SQLite has no InnoDB 3072-byte index limit, no utf8mb4 byte arithmetic, and it reports a broken migration with a different error than MySQL does. Every migration defect this addon has shipped was invisible under SQLite alone. Point the identical suite at a throwaway MySQL database with:

CI runs both, on both drivers.

Building the Control Panel assets

End users never need this — the compiled assets are committed under resources/dist/. But if you change anything in resources/js/ or resources/css/, rebuild and commit:

For a live dev loop against a real Statamic install, use scripts/setup-playground.sh (see below) and run npm run dev in the repo root.

End-to-end smoke test

Pest covers the domain layer. To verify the full pipeline against a real Statamic install — auto-discovery, migrations, the SubmissionCreated listener, both drivers, and the leadhub:storage:migrate command — run the bundled smoke test:

In ~3–5 minutes the script:

  1. Installs a fresh Statamic v6 project at /tmp/leadhub-smoketest-{ts}/
  2. Wires this LeadHub repo as a Composer path repository
  3. Configures SQLite, runs migrations, publishes the config
  4. Creates a contact form (blueprint + form yaml)
  5. Eloquent driver — submits Form::find('contact')->makeSubmission()->save(), asserts the contact landed in the DB
  6. Migration — runs php artisan leadhub:storage:migrate --from=eloquent --to=flat, asserts YAML files appear under content/leadhub/
  7. Flat driver — flips LEADHUB_DRIVER=flat, warms the Stache, submits a second form, asserts both contacts are visible to the flat repository

Configurable via env vars:

The script exits non-zero on the first failed step and leaves the broken project in place so you can cd in and poke around. After the run you can open the CP with:


Roadmap

Shipped beyond the core MVP: lead assignment + e-mail notifications, marketing attribution, CRM connectors (HubSpot / Brevo / webhook) with a sync log, a full outbound event surface, the CRM-core modules (companies, tasks, pipelines, scoring, segments, merge), an editable settings screen and a page per deal with its stage history.

Still on the table, not yet shipped:

Have a use case? Open an issue.


Contributing

Pull requests welcome. Please:

  1. Open an issue first to discuss the change
  2. Add tests for new domain behavior
  3. Keep PR scope tight — one concept per PR

License

Commercial license, © goldnead. See LICENSE.


All versions of statamic-leadhub with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
laravel/framework Version ^12.0|^13.0
statamic/cms Version ^6.0
inertiajs/inertia-laravel Version ^1.0|^2.0
symfony/yaml Version ^6.0|^7.0|^8.0
goldnead/statamic-brand-context Version ^1.13
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package goldnead/statamic-leadhub contains the following files

Loading the files please wait ...