# Platform naming constraints

Every value below was read from the vendor's live docs on 2026-10-17. Names, reserved lists and limits change. Before you quote one to a user, fetch the page listed in `sources.yaml` and confirm it. If the live page differs, follow it and say what changed.

## Contents

- The naming budget when tools share names
- GA4
- Mixpanel
- Amplitude
- PostHog
- Segment as the producer of names for other tools
- Google Tag Manager as a naming layer
- Vendor MCP servers and skills that touch names

## The naming budget when tools share names

- GA4 is the strictest destination for event name syntax and length. A canonical event name that passes GA4 passes the documented event name rules of Mixpanel, Amplitude, PostHog and Segment.
- That does not hold for parameter names. `bucket`, `token`, `time`, `ip`, `alias` and `distinct_id` are valid GA4 parameter names and Mixpanel reserved properties; `revenue`, `value` and `currency` are Segment reserved Track properties; Amplitude fields such as `session_id` belong at the top level of the payload, not in custom properties. Run P03, P06 and P07 on every property list.
- The house pattern (lowercase ASCII snake_case, object then past-tense action, 40 characters or fewer) is a recommendation, not a vendor requirement. Each vendor publishes a different style:
  - Mixpanel: snake_case, (Object) (Verb).
  - Amplitude: [Noun] + [Past-Tense Verb]; its own default events use Title Case.
  - Segment: Object + Action in Title Case for events, snake_case for properties.
  - PostHog gives two styles: lowercase snake_case, present-tense verbs and `category:object_action` on its best-practices page; `[object] [verb]` with past-tense examples (`project created`, `user signed up`) on its capture-events page.
- Two vendor styles do not fit GA4 unchanged: Title Case with spaces (Segment, Amplitude display style, PostHog's capture-page examples) and PostHog's colon. Keep the canonical name GA4-safe and use display names for the readable form.
- GA4, Mixpanel and Amplitude document event names as case sensitive. Case variants of one action become separate events. Treat PostHog the same until a test event confirms otherwise.

## GA4

Sources: event naming rules, collection limits, Measurement Protocol reference and validation server, Firebase Analytics event reference, enhanced measurement, recommended events, custom dimension limits, key events help, transaction ID deduplication.

Event name syntax:

- Starts with a letter. Letters, numbers and underscores only. No spaces.
- Case sensitive: `my_event` and `My_Event` are two events.
- Google's rule allows non-English letters. The house rule keeps canonical names to ASCII `a-z`, `0-9` and `_` so every downstream tool and SQL filter treats them the same way.
- 40 characters at most, for tag and SDK collection and for the Measurement Protocol. The limit cannot be raised in Analytics 360.
- A key event whose name is longer than 40 characters is not reported as a key event.

Reserved event names (Google says the lists are not exhaustive and may change; using one produces an error):

- Web streams: `ad_impression`, `app_remove`, `app_store_refund`, `app_store_subscription_cancel`, `app_store_subscription_renew`, `click`, `error`, `file_download`, `first_open`, `first_visit`, `form_start`, `form_submit`, `in_app_purchase`, `page_view`, `scroll`, `session_start`, `user_engagement`, `view_complete`, `video_progress`, `video_start`, `view_search_results`.
  - The help page lists `view_complete`. Enhanced measurement sends `video_complete`. Treat both as reserved.
  - `ad_impression` is only available on app streams. `in_app_purchase` is reserved on web streams and no longer on app streams.
  - Automatically collected names from the list can still be used as the source or condition when creating or modifying events in the GA4 interface.
- Web streams also reserve the event name prefix `query_id`.
- App streams (Android and iOS): `ad_activeview`, `ad_click`, `ad_exposure`, `ad_query`, `ad_reward`, `adunit_exposure`, `app_clear_data`, `app_exception`, `app_install`, `app_remove`, `app_store_refund`, `app_update`, `app_upgrade`, `dynamic_link_app_open`, `dynamic_link_app_update`, `dynamic_link_first_open`, `error`, `firebase_campaign`, `firebase_in_app_message_action`, `firebase_in_app_message_dismiss`, `firebase_in_app_message_impression`, `first_open`, `first_visit`, `notification_dismiss`, `notification_foreground`, `notification_open`, `notification_receive`, `notification_send`, `os_update`, `session_start`, `user_engagement`.
- The Firebase Analytics Event reference and the GA4 app-stream list differ in both directions; the SDK drops a reserved name and logs an error event instead. Firebase lists `app_background`, `app_store_subscription_convert` and `session_start_with_rollout`, which neither GA4 list has, and `app_store_subscription_cancel` and `app_store_subscription_renew`, which GA4 lists for web streams only. The GA4 app list has `app_install`, `notification_send`, `firebase_campaign` and the `firebase_in_app_message_` events, which Firebase does not. The lint script uses the union of the GA4 web, GA4 app and Firebase lists; refresh it from all three pages.
- The Measurement Protocol reference adds that `ad_impression`, `in_app_purchase` and `screen_view` are only allowed for app streams.
- The Firebase SDK reference reserves the event name prefixes `firebase_`, `google_` and `ga_`. `query_id` is a GA4 web stream prefix only. The lint rules reject all four for every stream type, because a canonical name may reach an app stream later.
- Google's own events sent with their documented meaning are destination mappings, not custom names; record each as an exception:
  - A manual `page_view` for a single-page app on a web stream.
  - `ad_impression` for ad revenue from an ad network on an app stream (the Firebase SDK has an `AD_IMPRESSION` constant).
  - `screen_view` for manual screen tracking on an app stream (the Firebase SDK has a `SCREEN_VIEW` constant).

Parameter, user property and item names:

- Parameter names: 40 characters. Values: 100 characters, except `page_title` (300), `page_referrer` (420) and `page_location` (1,000). The Measurement Protocol reference gives 500 characters for parameter values on 360 properties.
- 25 parameters per event. Up to 27 custom item-level parameters on ecommerce events, beyond the prescribed ones.
- Parameter names cannot start with `_`, `firebase_`, `ga_`, `google_` or `gtag.`. The Measurement Protocol also reserves `firebase_conversion`.
- Reserved parameter names that cannot be used for custom dimensions or metrics: `cid`, `currency`, `customer_id`, `customerid`, `dclid`, `gclid`, `session_id`, `sessionid`, `sfmc_id`, `sid`, `srsltid`, `uid`, `user_id`, `userid`. Keep `currency` for its standard meaning only.
- User properties: 25 per property; name 24 characters; value 36 characters. Names cannot start with `_`, `firebase_`, `ga_` or `google_`.
- Reserved user property names: `cid`, `customer_id`, `customerid`, `first_open_after_install`, `first_open_time`, `first_visit_time`, `google_allow_ad_personalization_signals`, `last_advertising_id_reset`, `last_deep_link_referrer`, `last_gclid`, `lifetime_user_engagement`, `non_personalized_ads`, `session_id`, `session_number`, `sessionid`, `sfmc_id`, `sid`, `uid`, `user_id`, `userid`.
- Reserved item parameter names that cannot be item-scoped custom dimensions (Google's list of 25): `affiliation`, `cid`, `creative_name`, `currency`, `customer_id`, `customerid`, `item_brand`, `item_category` to `item_category5`, `item_id`, `item_list_id`, `item_list_name`, `item_name`, `item_variant`, `promotion_id`, `promotion_name`, `session_id`, `sessionid`, `sid`, `uid`, `user_id`, `userid`. Standard fields such as `price`, `quantity`, `coupon`, `discount`, `index`, `location_id` and `creative_slot` are not on it.
- User-ID value: 256 characters.

Volume limits that a naming habit can hit:

- Distinct event names: no limit on web streams; 500 per app user on app streams (automatically collected and enhanced measurement events do not count). Dynamic names such as `step_3_completed` burn this budget on apps.
- Data over a limit is not logged. The request is not rejected; the event, parameter or user property simply disappears.
- Custom definitions on a standard property: 50 event-scoped dimensions, 25 user-scoped, 10 item-scoped, 50 custom metrics. After deleting dimensions at the limit, wait 48 hours before adding new ones. Google warns against high-cardinality dimensions such as timestamps and session IDs.
- Key events: 30 on a standard property, 50 on 360. Marking is not retroactive. An event can be marked before it is first received.

Recommended event names carry Google's meaning and parameters. Map a canonical event to one only when the trigger matches:

- All properties: `earn_virtual_currency`, `join_group`, `login`, `search`, `select_content`, `share`, `sign_up`, `spend_virtual_currency`, `tutorial_begin`, `tutorial_complete`.
- Online sales: `add_payment_info`, `add_shipping_info`, `add_to_cart`, `add_to_wishlist`, `begin_checkout`, `purchase`, `refund`, `remove_from_cart`, `select_item`, `select_promotion`, `view_cart`, `view_item`, `view_item_list`, `view_promotion`.
- Lead generation: `generate_lead`, `qualify_lead`, `disqualify_lead`, `working_lead`, `close_convert_lead`, `close_unconvert_lead`.
- Games: `level_start`, `level_end`, `level_up`, `post_score`, `unlock_achievement`.
- `purchase` requires `transaction_id` and `items`; `currency` is required when `value` is set. `sign_up` and `login` take an optional `method`.

Key events that GA4 marks automatically (key events help page, checked 2026-10-17). Mapping a canonical event to one of these names can create a key event nobody chose, inflate the all-key-events rate and feed a Google Ads conversion:

- Every property: `purchase`.
- App streams: `first_open`, `in_app_purchase`, `app_store_subscription_convert`, `app_store_subscription_renew`.
- With a linked Google Ads account and Ads Personalization on: `add_to_cart`, `add_to_wishlist`, `begin_checkout`, `session_start`, `view_item`, `view_item_list`, `view_search_results`.
- Before mapping to one of them, check the property's key events list and the Google Ads link with its Ads Personalization setting. Record in the mapping whether the GA4 name will become a key event and which Ads conversion it would feed, and get the conversion owner's approval. Lint rule M07 flags these names.

Interface features that change names (details in `renames-and-migrations.md`): Modify event and Create event, both under Admin, Data display, Events. Modify event does not apply to Measurement Protocol traffic.

Deduplication: GA4 has no general event deduplication. It deduplicates `purchase` events with the same `transaction_id`, on web streams only (no window is stated). Never send an empty `transaction_id`: GA4 deduplicates every purchase with `transaction_id=""` against each other.

Checking names without collecting data: the Measurement Protocol validation server (`https://www.google-analytics.com/debug/mp/collect`) returns `validationMessages` with codes such as `NAME_INVALID`, `NAME_RESERVED`, `VALUE_OUT_OF_BOUNDS` and `EXCEEDED_MAX_ENTITIES`; an empty array means no issues. Events sent to it do not appear in reports. It does not validate `api_secret` or `firebase_app_id`, and the page does not say whether it validates `measurement_id`, so confirm at run time that a placeholder ID still returns validation results. It applies Measurement Protocol rules, so an event sent only by a tag still needs a DebugView check.

## Mixpanel

Sources: events and properties page, reserved properties page, import events API, Lexicon, Data Standards, MCP docs.

- Case sensitive: `sign_up_completed` and `Sign_Up_Completed` are two events.
- Mixpanel recommends snake_case and the (Object) (Verb) format, and warns against dynamic names such as a date inside the event name.
- Up to 255 properties per event (the import API says fewer than 255). Property names and values longer than 255 characters are truncated. No separate event-name length limit was documented on 2026-10-17; the import API truncates all strings to 255 characters.
- Soft limits: 5,000 distinct event names and 5,000 distinct event properties per project. Past them, data is ingested but not indexed and not shown in menus.
- Reserved event properties include `token`, `ip`, `distinct_id` and `$distinct_id`, `$device_id`, `$user_id`, `time` and `$time`, `$insert_id`, `alias`, and `bucket` and `$bucket`. Mixpanel says never to name a property `bucket` or `$bucket`; `$bucket` stops other events appearing in the interface.
- Mixpanel-defined events start with `$`.
- Deduplication needs identical event name, `time`, `distinct_id` and `$insert_id`. Because the name is part of the key, an old and a new name never deduplicate against each other.
- Lexicon behaviour that matters for naming is in `renames-and-migrations.md`. In short: display names are UI only; raw names cannot be renamed after ingestion; merges do not touch raw data and add the merged series together; hidden events are excluded from query results; a block (owner only) stops storing new data and can be undone with Unblock, but data sent while blocked is never stored, and history is kept.
- Data Standards (Enterprise; defined by project admins or owners) gives each event a Compliancy status in Lexicon against a naming syntax (the docs give snake_case and camelCase as examples), description, owner and media rules. The docs describe status only, not blocking. It evaluates the display name when one is set, so a friendly display name can make a non-conforming raw name look compliant. Lint the raw names separately.

## Amplitude

Sources: data planning playbook, HTTP V2 API, limits and quotas, Amplitude Data settings, display names, transformations, remove invalid data, MCP docs.

- Case and word order matter: `Song Played`, `song played` and `Played Song` are three events.
- Amplitude recommends [Noun] + [Past-Tense Verb] from the user's perspective and allows any convention applied consistently.
- Reserved event names: `[Amplitude] Start Session`, `[Amplitude] End Session`, `[Amplitude] Revenue`, `[Amplitude] Revenue (Verified)`, `[Amplitude] Revenue (Unverified)`, `[Amplitude] Merged User`. `$identify` and `$groupidentify` are predefined for identification.
- String values, including the event type, are limited to 1,024 characters; most property values are truncated past that.
- Per project: 2,000 event types, 2,000 event properties, 1,000 user properties. Past these, Amplitude stops indexing new values, they cannot be queried in charts, and they are reachable only through export. Dropdowns show the first 1,000 values of a property.
- The HTTP V2 page says `session_id` must sit at the top level of the event payload, otherwise Amplitude cannot map it. It does not say how a key of the same name inside `event_properties` behaves, so avoid naming custom properties after top-level fields (`session_id`, `insert_id`, `time`).
- Amplitude Data naming convention setting: lower case, Sentence case, Title Case, CamelCase, lowerCamelCase, snake_case or a custom convention, on all plans. The page does not say whether non-conforming names are flagged or blocked; check in the project.
- Schema settings can mark unplanned events and properties as unexpected (ingested, subscribers notified) or reject them (not ingested or stored, subscribers notified). A renamed event that is not in the plan is lost under reject.
- Deduplication uses `insert_id` on the same `device_id` within 7 days. The event type is not part of the key.

## PostHog

Sources: product analytics best practices, capture events page, events page, actions page, MCP docs.

- PostHog's own guidance conflicts. The best-practices page recommends lowercase snake_case, present-tense verbs, a fixed list of allowed verbs and the `category:object_action` framework. The capture-events page recommends `[object] [verb]` with past-tense examples (`project created`, `user signed up`, `invite sent`). Pick one for the project and record it; the house default is the past-tense form in snake_case.
- Property names (best-practices page) use `object_adjective`, `is_` or `has_` for booleans, and `_date` or `_timestamp` suffixes.
- The colon in `category:object_action` fails GA4 syntax. When PostHog shares canonical names with GA4, drop the category prefix (put it in a property) or map per destination.
- Events starting with `$` are PostHog defaults.
- Case sensitivity of event names was not stated on the pages checked. Treat names as case sensitive and confirm with a test event.
- Actions combine several events into one for analysis and apply retroactively; a single-event action can act as a readable "rename".

## Segment as the producer of names for other tools

Sources: collection best practices, Track spec, GA4 Cloud destination, Protocols transformations, duplicate data guide.

- Segment recommends Object + Action, Title Case event names and snake_case property names, and warns against dynamic event names and dynamic property keys (`feature_1: true`).
- Reserved Track properties keep their meaning: `revenue` (decimal number), `currency` (ISO 4217 code; Segment assumes US dollars when missing), `value` (abstract value).
- GA4 Cloud destination: Lowercase Event Name is an optional field on each Custom Event action mapping, not a destination setting, and no default was stated on 2026-10-17. The Custom Event action replaces spaces in its Event Name with underscores. `Report Created` reaches GA4 as `Report_Created` or `report_created` depending on that mapping's field, so two Custom Event mappings can lowercase differently. Record the value per mapping and lint the final names.
- Prebuilt actions (Purchase, Sign Up, Page View, Add to Cart and others) have no Event Name field, so the received name is not set in the mapping. Confirm it in Realtime or the BigQuery export.
- GA4 Cloud is a server-side integration with the GA4 Measurement Protocol. Consequences for naming:
  - GA4 Modify event does not apply to it: Google's help page says events sent server to server cannot be modified. That page does not say whether Create event applies; test before relying on it, and never use Create event to rename. Fix a Segment-routed name in the Custom Event mapping (Event Name or Lowercase Event Name) or with a destination-scoped Protocols transformation.
  - Measurement Protocol rules apply: reserved names and prefixes are silently dropped, and `ad_impression`, `in_app_purchase` and `screen_view` are allowed only for app streams.
  - Segment says GA4 debug mode works only with gtag.js, Google Tag Manager or Firebase, so DebugView does not show these events. Verify a name with the Measurement Protocol validation server (`/debug/mp/collect`), then in Realtime or the BigQuery export.
- Segment deduplicates on `messageId` only, not on payload, and is not scoped to a source or workspace. It stores at least 24 hours of `messageId` values; warehouses deduplicate on `messageId` beyond 24 hours.

## Google Tag Manager as a naming layer

Sources: GTM GA4 event tag setup, Custom Event trigger, built-in variables, publishing and versions, Tag Manager API authorization (checked 2026-10-17).

- On web, the name GA4 receives is usually the Event Name field of a GA4 Event tag, not the name in code. The dataLayer event (`dataLayer.push({event: 'reportExported'})`) only fires the trigger. Record both: the dataLayer event and the GA4 Event Name, as separate columns.
- An Event Name set to the built-in `{{Event}}` variable forwards the raw dataLayer event name, which can be camelCase, a vendor's own push, or `gtm.js`, `gtm.dom` and `gtm.load` (the `Event` variable's documented values) when the trigger is broad. Lint the Event Name values extracted from the container, and treat any variable in the field as a finding.
- Other ways a container rewrites names: a Lookup Table or RegEx Table variable in the Event Name field; one dataLayer event firing two GA4 Event tags with different names (M08 on the mapping); a server-side GTM client or tag that renames before GA4.
- A Custom Event trigger matches the dataLayer event name (exactly, unless "use regex matching" is selected). Renaming a dataLayer event silently stops every tag on that trigger. Google's page does not state whether the match is case sensitive; verify in Preview before relying on either.
- Renaming in GTM: change the Event Name in a workspace, check it in Preview (Tag Assistant) and DebugView, and publish a new container version. Every publish records a version with its publish date in the Versions screen; that publish time is the rename boundary. Earlier versions can be restored if the change misfires.
- Reading a container: an export JSON, the gtm-container-audit skill, or the Tag Manager API with the `tagmanager.readonly` scope (view only). Scopes that edit, publish or delete are not needed for naming work.

## Vendor MCP servers and skills that touch names

Use read tools for audits. Write tools change shared configuration; they run only after the user approves a written change list.

- GA4: Google's GA4 MCP server (labelled Experimental) authenticates with the `analytics.readonly` scope. Tools include `run_report` (event names and counts), `run_realtime_report` and `get_custom_dimensions_and_metrics`. It cannot rename or modify events.
- Mixpanel: remote MCP at `mcp.mixpanel.com` (EU and IN hosts exist). Read: `Get-Events`, `List-Properties`, `Get-Property-Values`, `Get-Issues`, and `Find-Duplicate-Groups` (finds duplicate and near-duplicate event and property names; the docs describe it as discovery and do not label it read or write). Write: `Edit-Event` and `Bulk-Edit-Events` (description, display name, tags, visibility; up to 50 at once), `Edit-Property`, `Bulk-Edit-Properties`, `Dismiss-Duplicate-Group`, `Merge-Group`. No read-only mode is documented. Mixpanel's `manage-lexicon` skill (mixpanel/ai-plugins) audits and cleans Lexicon metadata; its `tracking-implementation` skill guides instrumentation.
- Amplitude: remote MCP at `mcp.amplitude.com` (EU host exists). Read: `get_amp_taxonomy`, `get_transformations`. Write: `manage_amp_events`, `manage_amp_properties`, `manage_amp_taxonomy` (branches). Read-only access: an admin gives the user a role (Org Settings, Role Management) with the Use MCP (read) action and without Use MCP (write). Writes need Use MCP (write) plus Update Tracking Plan. Deletes on main take effect immediately; on a branch they wait for the merge. Amplitude's `taxonomy` and `tracking-plan-audit` skills (amplitude/mcp-marketplace) audit naming; `instrument-events` and `add-analytics-instrumentation` write tracking code.
- PostHog: remote MCP at `mcp.posthog.com`. Read-only mode: header `x-posthog-read-only` or query parameter `readonly`, set to `true` or `1`.
- Segment: Twilio's hosted MCP (`mcp.twilio.com/docs`) only searches and retrieves public API specs and docs, including Segment docs; it executes nothing. Twilio's MCP page mentions no Segment-specific server (checked 2026-10-17); check again at run time. Protocols and transformations are changed in the Segment app or Public API by a workspace owner or source admin.
