Owlmetry

User Properties

Attach custom key-value metadata to users — subscription status, revenue, plan tier, and more.

User properties are custom key-value pairs stored on each user in Owlmetry. They let you see at a glance who is a paid subscriber, what plan they're on, how much they've spent, or any other user-level attribute you care about.

Properties are stored on each project-level user and displayed in the Users list in the dashboard. Users are unique per project — the same user ID seen from multiple apps (e.g., iOS and backend) is a single user with one set of properties.

How It Works

Properties are merged, not replaced. When you set { plan: "premium" }, it merges with existing properties. Setting a value to an empty string "" deletes that key.

Existing:  { plan: "free", org: "acme" }
Set:       { plan: "premium", role: "admin" }
Result:    { plan: "premium", org: "acme", role: "admin" }

Setting Properties

From the SDK

Both the Swift and Node SDKs provide setUserProperties():

// Swift
Owl.setUserProperties(["plan": "premium", "org": "acme"])
// Node.js
const owl = Owl.withUser("user_123");
owl.setUserProperties({ plan: "premium", org: "acme" });

From Integrations

Third-party integrations like RevenueCat automatically sync subscription data into user properties via webhooks. See Integrations for setup.

Via the API

POST /v1/identity/properties
Authorization: Bearer owl_client_...

{
  "user_id": "user_123",
  "properties": {
    "plan": "premium",
    "org": "acme"
  }
}

Limits

LimitValue
Max properties per user50
Max key length50 characters
Max value length200 characters
Value typeStrings only

Properties vs Event Attributes

Use user properties for data that describes the user and changes infrequently (plan tier, subscription status, company name). Use event custom attributes for data specific to a single event (button clicked, page URL, error code).

User PropertiesEvent Attributes
ScopeUser-levelEvent-level
PersistenceStored on the user, visible in Users listStored on each event
UpdatesMerged on each callImmutable after ingest
Use caseSubscription status, plan tierAction details, error context

Identity Claim Merge

When an anonymous user is claimed (Swift Owl.setUser(), or direct POST /v1/identity/claim), properties from the anonymous user are merged into the real user. The real user's existing values take precedence on key conflicts.

Reserved Namespaces

Some property keys are populated by Owlmetry itself and should not be written manually from your app code. Overwriting them is allowed (properties are just strings), but you'll collide with the values integrations and attribution write.

Prefix / KeyPopulated byPurpose
rc_*RevenueCat integrationSubscription status, plan, entitlements, billing period
attribution_sourceAttribution subsystemWhich network attributed the install (apple_search_ads, none, apple_test_install). The apple_test_install value is set with no asa_* fields when Apple returns its non-production fixture — TestFlight, Xcode-deployed dev builds, or the simulator.
asa_*_id / asa_claim_typeSwift SDK (auto-captured)Apple Search Ads IDs (campaign, ad group, keyword, ad, creative set) plus claim type, resolved from Apple's AdServices API
asa_campaign_name, asa_ad_group_name, asa_ad_name, asa_keywordApple Search Ads integration (primary) or RevenueCat integration (secondary)Human-readable names for the same campaign / ad group / ad the asa_*_id fields identify, plus the literal search keyword. Apple's AdServices Attribution API returns only numeric IDs; both sources resolve those IDs → names via Apple's ASA Campaign Management API (ASA integration does it directly; RC does it server-side and surfaces resolved names as subscriber attributes). Both cover every attributed user, subscriber or not.

Attribution properties are captured once per anonymous user by the Swift SDK and carried through the claim merge into the real user. To opt out, set attributionEnabled: false on Owl.configure(). See Attribution for the full pipeline.

Backfill paths (RC sync/webhook and ASA integration sync) are additive: they never overwrite an asa_*_id or attribution_source set by the Swift SDK's live flow — they only fill empty slots. For users who predate the Apple AdServices capture, these backfill paths are the only source of attribution.

Dashboard columns

Attribution fields (asa_campaign_name, asa_ad_group_name, asa_keyword, asa_ad_name) are exposed as opt-in columns on the Users page via the column picker. Open the picker, toggle any attribution column on, drag to reorder — your configuration is persisted per-user via users.preferences so it follows you across browsers and devices. The same picker works for the Events page, letting you build personal views that stay out of your teammates' way.

Ready to get started?

Connect your agent via MCP or CLI and start tracking.