Email tracking
Swap mailto: links to per-visitor tracked addresses. Replies come back through Funnelion.ai, attributed to the originating campaign.
Every visitor session claims a unique address on a domain you control (e.g. antanas@inbox.yoursite.com). When the visitor sends mail to that address, Funnelion.ai records a lead event tied to their traffic source and forwards the mail to the inbox you operate from day-to-day.
1. Dashboard setup
- Add an email domain at Email domains → Add. Pick a subdomain you don’t already use for mail - e.g.
inbox.yoursite.comormail.yoursite.com. - Publish the MX / SPF / DKIM DNS records Funnelion.ai shows you. Wait for the row to flip to “verified” (a couple of minutes once DNS propagates).
- Create a dynamic email pool at Pools → New: channel
email, typedynamic, domain you just verified, one or more language packs (or custom names), forward-to recipient, reply modeintercept. - Create a swap zone at Swap zones → New: kind
email, selectora[href^="mailto:"], pool you just created. - Optional: add routing rules so submissions from specific UTMs get a custom source label (“Facebook”, “Google”, etc.).
Why a separate subdomain?
yoursite.com) and never the host you already receive primary mail on. inbox., mail., track. are common picks.2. Frontend path (JS snippet)
Drop the snippet into your site’s <head>. It binds to every mailto: link on the page and swaps the address (and the visible text) with the visitor’s claimed pool address. Ships zero requests until a real visitor hits the page.
<!-- Production (minified, recommended) -->
<script async src="https://dash.funnelion.ai/track.min.js?t=YOUR_SITE_TOKEN"></script>
<!-- Same code, unminified - handy when debugging in DevTools -->
<script async src="https://dash.funnelion.ai/track.js?t=YOUR_SITE_TOKEN"></script>Your SITE_TOKEN is the public token on the Site row in the dashboard - safe to embed in HTML. For specific anchors, add data-funnelion="Email Address" (the zone name from step 4 above) so the snippet knows exactly which element to swap:
<a href="mailto:hello@yoursite.com" data-funnelion="Email Address">
hello@yoursite.com
</a>The visible text inside the link is your fallback - what the visitor sees if Funnelion.ai is unreachable. Always write a real working address there.
3. Backend path (PHP SDK)
Server-side resolution survives ad blockers, content blockers, and strict CSPs because the visitor’s browser never has to reach Funnelion.ai. Requires PHP 8.1+ and ext-curl.
Install the SDK
composer config repositories.funnelion vcs https://github.com/funnelion/funnelion-php.git
composer require funnelion/sdk:^0.3.0Wire it into your page render
Read the server-side token from the dashboard (Sites → Edit → Server-side token) and put it in your server’s .env. Then, in your front controller / page renderer:
<?php
require __DIR__.'/vendor/autoload.php';
use Funnelion\Client;
use Funnelion\Config;
use Funnelion\Cookie\Session;
use Funnelion\Html\ZoneSwapper;
use Funnelion\Resolve\Request;
$client = new Client(new Config(
siteToken: getenv('FUNNELION_SERVER_SIDE_TOKEN'),
timeoutSeconds: 0.5,
));
$response = $client->resolveOrNull(new Request(
url: 'https://yoursite.com'.($_SERVER['REQUEST_URI'] ?? '/'),
ip: $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0',
referrer: $_SERVER['HTTP_REFERER'] ?? null,
userAgent: $_SERVER['HTTP_USER_AGENT'] ?? null,
visitorId: Session::readFromGlobals(),
));
if ($response !== null) {
$html = (new ZoneSwapper())->swap($html, $response);
if ($response->visitorId !== null) {
header('Set-Cookie: '.Session::headerValue($response->visitorId), false);
}
}
echo $html;Mark up your HTML
The ZoneSwapper looks for data-funnelion="<Zone Name>" on a leaf element (text only, no nested tags). For <a> tags with a mailto: href, both the href and the visible text get rewritten.
<a href="mailto:hello@yoursite.com" data-funnelion="Email Address">
hello@yoursite.com
</a>React / Vue hydration gotcha
<svg> child), the server-side swapper can’t touch it - its regex requires a leaf element. The fix: render the resolved address in JSX from window.__FUNNELION_ZONES (the SDK injects this script tag into your HTML for exactly this case). See src/pages/Contact.jsx in the funnelion.ai source repo for a working pattern.4. What happens when mail arrives
Inbound mail to any address on your verified domain is parsed by Funnelion.ai and ingested as a lead event of kind email, attributed to the visitor session whose cookie was set when they were on your page. The mail is then forwarded to the recipient you configured on the pool - your real inbox.
- Reply mode
interceptrewrites Reply-To headers so visitor responses still come back through Funnelion.ai (full thread visibility). - Reply mode
pass_throughforwards the mail as-is - Funnelion.ai sees the inbound event but drops out of the loop after that.