Attach your own key-value data and tags to visitors
A user profile is the record Transfon Security keeps for each visitor of a Tag Mode project. On its own the profile only contains what the tag can observe — IP addresses, user agents, page views. With the JavaScript API you can attach your own key-value data to it: an email address, a plan name, a customer ID, lifetime value, tags such as beta or vip.
Once attached, that data is shown on the User Profiles tab and in the Custom Data & Tags section of the profile panel, and it can be used to filter events and profiles.
User profiles are available in Tag Mode projects only. Gateway Mode projects log requests without a client-side tag, so there is no browser to send custom data from.
The tag stores a random UUID in a first-party cookie named __unis_uid (valid for 301 days) and sends it with every event. A profile is keyed by project + UUID, so:
Because the UUID is anonymous until you say otherwise, sending email (or your own customer ID) is what ties a profile to a real account.
Custom data is sent with the reserved event name sync:
window._waf360.send("sync", {
email: "[email protected]",
name: "Jane Doe",
plan: "pro",
mrr: 49.9,
seats: 12,
approved: true,
signup_date: "2025-03-14",
segments: ["beta", "eu"]
});
Key points:
sync events write to the profile. Any other event name (pv, signup, your own custom events) is recorded as an event, but its payload is not merged into the profile.There is no need to resend the whole profile on every page. A later sync updates the keys it carries and leaves every other key untouched:
// on login
window._waf360.send("sync", { email: "[email protected]", name: "Jane Doe" });
// later, after an upgrade — email and name are kept
window._waf360.send("sync", { plan: "enterprise", mrr: 499 });
For a scalar key (string, number, boolean, date) the last value wins. Array values behave differently — see Tags.
Two keys are used by the dashboard itself:
| Key | Used for |
|---|---|
email | Profile subtitle, the Email filter on the User Profiles tab, and the email column in the events table |
name | The display name at the top of the profile card and panel |
Every other key is stored as custom data and shown in the Custom Data & Tags section of the profile panel.
Each value keeps a type, which is what makes numeric and date comparisons possible. The type is inferred from the value you send:
| What you send | Stored as | Example |
|---|---|---|
true / false | boolean | { approved: true } |
| Whole number | integer | { seats: 12 } |
| Fractional number | float | { mrr: 49.9 } |
| Array | string array (tags) | { segments: ["beta", "eu"] } |
| Other string | string | { plan: "pro" } |
| Object | string (serialized) | avoid — flatten it yourself |
Strings are inspected before being stored, so a string that looks like a boolean, a number or a date is converted to that type:
"true", "false", "1", "0", "t", "f" become booleans"12" becomes an integer, "49.9" a float"2025-03-14", "2025-03-14 09:30:00" and RFC 3339 / RFC 1123 / RFC 822 timestamps become datetimesThis matters for identifiers. A customer ID sent as { customer_id: "1" } is stored as the boolean true, and { customer_id: "00042" } is stored as the integer 42. If a value is an identifier rather than a quantity, prefix it so it stays a string:
window._waf360.send("sync", { customer_id: "cus_00042" });
Do not send null — it is stored as the literal text <nil>. Omit the key instead, or send an empty string to blank it out.
Any array value is treated as a set of tags. Later sync calls add to the set instead of replacing it, and duplicates are ignored:
window._waf360.send("sync", { segments: ["beta"] });
window._waf360.send("sync", { segments: ["eu", "beta"] });
// profile now holds: segments = ["beta", "eu"]
To remove a tag, send the value prefixed with a hyphen:
window._waf360.send("sync", { segments: ["-beta"] });
// profile now holds: segments = ["eu"]
Because of this, tags are the right shape for anything a visitor can gain and lose over time — trial cohorts, feature flags, lifecycle stages — while a plain string is the right shape for a value that is simply replaced.
Alongside your own attributes, a profile holds what Transfon Security observes about the visit: the page and referrer, the user agent, the network and location, and the traffic quality signals. Those observations use key names of their own, and a value you send under one of them is replaced by the observed one.
So keep your keys unmistakably yours and avoid generic request-shaped names such as url, ua, country, ip or tags. A short prefix is the simplest way to stay clear:
window._waf360.send("sync", {
acct_plan: "pro",
acct_segments: ["beta", "eu"]
});
email and name are the exception — send those under their own names, since they are the keys the dashboard reads.
name (or email, or the UUID when neither is set), country, IP, event count and last-seen time. Use the Email filter to find one account.sync) and Recent Activity across the organization's Tag Mode projects.In both panels, scalar attributes are listed as key-value pairs and array values are shown as tag chips, grouped under the key you sent them with.
A typical integration renders one call from the server-side session on every authenticated page:
<script>window._waf360={q:[],send:(w,c)=>window._waf360.q.push([w,c]),check:w=>window._waf360.q.push(["check",w])};</script>
<script id="waf360-tag" src="https://tag.waf360.com/tag/tag.js" data-license="YOUR_LICENSE_KEY" defer async></script>
<script>
window._waf360.send("sync", {
email: "{{ user.email }}",
name: "{{ user.full_name }}",
customer_id: "cus_{{ user.id }}",
plan: "{{ user.plan }}",
seats: {{ user.seats }},
approved: {{ user.is_approved ? 'true' : 'false' }},
signup_date: "{{ user.created_at|date('Y-m-d') }}",
segments: [{{ user.segments|join(', ') }}]
});
</script>
On a page where the visitor is anonymous, simply do not call sync — the profile keeps whatever it learned on earlier visits.
data-sample-rate below 1, data collection — including sync — is skipped on unsampled page views. Use data-sample-rate="1" (or omit it) on pages that send profile data.__unis_uid first-party cookie and your sync payload is stored against it, so gate the call behind consent where your jurisdiction requires it.Privacy, security, monetization, and performance insights. A few emails a month, no spam.