Telemetry data dictionary
Every field the SDK can send, and why. If a field is not on this page, it is not collected.
Last updated
Two streams, deliberately separate
The SDK sends two kinds of thing, and the difference between them is the whole privacy design rather than an implementation detail.
Telemetry is anonymous and continuous. It identifies a site only by an install ID, which is an HMAC of the site’s own address under a salt that never leaves that site — it cannot be reversed into an address, including by us.
Deactivation feedback is identified and one-off. It is a message an administrator chose to send, and it carries the site address, because a developer who cannot tell which site said “it broke my site” cannot act on it. The administrator sees the itemised payload before sending and can decline.
The join-key rule
No payload and no stored row may carry both an install ID and a site address. If one did, the anonymous stream could be joined to the identified one and every telemetry row that install had ever sent would be de-anonymised, retroactively.
So the feedback table has no install column, and the feedback payload omits the install ID and the token. The correlation is impossible rather than merely forbidden — there is no field to do it with.
Anonymous telemetry — every field
| Field | Type | Why it is collected |
|---|---|---|
install_id | string | Counts one site once. An HMAC of the site's own URL under a salt that never leaves that site, so it cannot be reversed to an address even by us. |
plugin_slug, plugin_version | string | Which plugin reported, and which release it is on. The version-adoption chart is this column. |
wp_version, php_version | string | Whether a version can be dropped without stranding people. |
locale | string | Distinguishes a translation problem from a usability one — "confusing" from an unsupported locale is not the same report. |
multisite | boolean | A fault on multisite is a different fault from the same message on a single site. |
server | string | Apache, Nginx or LiteSpeed. Which stacks the plugin has to behave on — the "server software mix" the Telemetry SDK page advertises is this column. NULL for an install running an SDK older than 1.2.0, because those copies are frozen inside published plugins and cannot be upgraded remotely. |
theme_slug | string | Themes cause as many conflicts as plugins do. The slug only — the version and the parent travel with deactivation feedback, where a bug report wants the whole picture, and are deliberately not columns on the largest table in the schema. |
props | json | Values for the opted-in custom events the plugin author instrumented. Keys and values are both checked against the allowlist; an unlisted key rejects the whole event rather than being dropped from it. |
consent_policy, consent_at | mixed | Which collection policy the site owner agreed to, and when. This is the record that the opt-in happened at all — without it "nothing is sent until consent" would be a claim with nothing behind it. |
occurred_at, received_at | datetime | When the site says it happened, and when we received it. Retention is measured from the second — a site's clock is not ours to trust. |
sdk_version, schema_version | string | The SDK ships bundled inside third-party plugins frozen at whatever version their author downloaded, so old payload shapes arrive forever and have to stay readable. |
The events that may be sent
A closed set. An event name that is not on this list is rejected at ingestion, and so is any property key not listed against its event — an unrecognised key rejects the whole event rather than being quietly stripped from it. The SDK enforces this too, but the server does not rely on that: a request does not have to come from our SDK.
| Event | Permitted properties |
|---|---|
install | — |
activate | — |
version | from |
compat | what, from |
feature | name, count |
deactivation | reason |
Deactivation feedback — every field
Sent once, when an administrator deactivates a plugin and chooses to say why. Nothing here is sent without that choice, and none of it travels with an install ID.
| Field | Type | Why it is collected |
|---|---|---|
site_url | string | The one identifying field the product collects. A developer who cannot tell which site said "it broke my site" cannot act on it. Kept only because the administrator reads it verbatim before sending, and only because no install id travels with it. |
reason | string | One of a closed set of deactivation reasons, plus optional free text the administrator typed. |
plugin_slug, plugin_version, wp_version, php_version, server | string | What was running when it broke. A conflict cannot be reproduced without it. |
theme_slug, theme_version, theme_parent | string | Themes cause as many conflicts as plugins do. |
plugins, total_plugins | json, int | Active plugins as basename and version, bounded to 100 entries (`API\Telemetry::PLUGINS_MAX`) — and the bound is re-applied server side, because a bound only the client enforces is not a bound. The count is stored beside the list so a truncated list is visibly truncated rather than silently short. |
locale, multisite | mixed | Same reasons as the telemetry stream. |
sdk_version, schema_version | string, int | Which build of the SDK produced the submission, and which payload shape it used. Bundled copies are frozen inside published plugins, so schema 1 submissions keep arriving indefinitely and have to stay readable. |
submitted_at, received_at | datetime | When the administrator pressed send, and when we received it. Listed for the same reason as the telemetry pair: a timestamp is data about a person even when it is only a timestamp. |
Never collected, by either stream
- Administrator or customer email addresses
- IP addresses
- Order, customer or page content
- Passwords, API keys or any credential
- Raw server or error logs
- Frontend visitor behaviour
- The site address in the anonymous telemetry stream — it is refused there, and kept only in deactivation feedback
Consent
Nothing is transmitted before a site owner opts in — not a heartbeat, not a version check. Deactivation feedback asks separately, because agreeing to anonymous counting is not agreeing to send a site address.
A site owner or host can stop all outbound transmission by defining CX_TRACKER_DISABLE. It is checked first and unconditionally, including by feedback — a switch with an exception in it would not be a switch.