Surfaces

Surfaces

A project is the top-level tenant in GetFluxly: it owns all identity data, API keys, and analytics for one product. A surface is a tracked property beneath a project, representing a distinct place where events originate, such as a marketing site, web app, docs site, or mobile app. Each surface has its own SDK keys, and every event that comes through a surface-bound key is stamped with that surface's ID at ingest time. Identity resolution always stays project-scoped, so users are unified across surfaces automatically.

How data is separated

When an API key is created, it is bound to a surface via the surface_id column on the api_keys table. At ingest, the server resolves the calling key to its ActiveApiKeyRecord, reads surface_id off the record, and writes it onto every analytics event. Events from legacy project-wide keys (those with no surface binding) are stored with a null surface ID and appear under "All surfaces" in the dashboard.

"All surfaces" is a UI alias, not a database entity. It means no surface filter is applied and every event for the project is included. Switching to a named surface in the dashboard header dropdown filters analytics to events from that surface only.

The filter selection persists per-project in the browser's localStorage, so the last surface you viewed is restored on your next visit.

Default event properties

A surface can define a default_event_properties JSON object. At ingest, these defaults are merged under each event's own properties using spread syntax:

payload["properties"] = {**surface_defaults, **event_props}

The event's own values always win on key conflicts. Default properties are a no-op if the surface defines none, or if the event already provides every key. A common use is auto-stamping a source field so you can distinguish surfaces in raw queries without filtering on surface_id:

{
  "source": "landing"
}

Managing surfaces

Create a surface

Go to Settings > Surfaces in the GetFluxly dashboard. Click New surface, enter a name, and optionally provide a slug. If you leave the slug blank, one is derived automatically from the name by lower-casing and replacing spaces with hyphens. Slugs must contain only lowercase letters, digits, and hyphens, with no leading or trailing hyphen, and no more than 100 characters. Slugs are unique per project.

Every new project is seeded with a primary surface named Main (slug main). You cannot lose this surface, and it behaves identically to any surface you create yourself.

Mint an SDK key bound to a surface

From the Surfaces list, click the key icon on any surface row to open the key generation dialog. Choose:

FieldOptionsNotes
Key typepublishable, serverPublishable keys are safe in browser bundles. Server keys are for backend use only.
Environmenttest, liveTest-environment events do not appear in the live analytics dashboard.

The full key is shown exactly once after creation. Copy it before closing the dialog.

See /authentication for how to pass the key in requests and for the difference between publishable and server keys.

Rename a surface

Click the pencil icon on the surface row. Renaming updates the display name only; the slug is unchanged, so existing events and any stored references continue to resolve correctly.

Filter the dashboard to a surface

Use the surface picker in the dashboard header to switch between named surfaces or "All surfaces". The selection is stored per-project in localStorage. The time-range picker works alongside the surface filter.

Soft delete

Deleting a surface sets deleted_at to the current timestamp. The row is not removed from the database, so historical events retain their surface_id reference and stay queryable. The slug remains reserved under the project, including after deletion. If you need a deleted surface restored, contact support.

Deleting a surface does not automatically revoke the SDK keys bound to it. If you want to stop events from a retired surface, delete or rotate the keys themselves.