Context for visitor CRUD and lifecycle operations.
Summary
Functions
Gets the attribution path for a visitor.
Removes all PII, events, and identity signals for a visitor (GDPR forget).
Gets a visitor by host-app external ID within a workspace.
Gets a visitor by ID.
Gets a visitor by ID, scoped to a workspace.
Lists recent visitors for a workspace.
Populates visitor.device for visitor_id if and only if the current value is
empty (first-event-wins), mirroring maybe_set_geo/2. Device parsing is
synchronous, so this is called inline at ingest rather than via async enqueue.
Populates visitor.geo for visitor_id if and only if the current value is
empty. Used by async ingest enrichment (beacon, REST API) where we want
first-event-wins semantics without re-running identity resolution.
Gets the event timeline for a visitor, ordered by inserted_at desc.
Updates visitor attribution fields (extension point for GoodPartners).
Updates the lifecycle status of a visitor.
Functions
Gets the attribution path for a visitor.
Removes all PII, events, and identity signals for a visitor (GDPR forget).
Clears all identifying fields and deletes associated events.
Gets a visitor by host-app external ID within a workspace.
Gets a visitor by ID.
Gets a visitor by ID, scoped to a workspace.
Lists recent visitors for a workspace.
@spec maybe_set_device(Ecto.UUID.t(), map()) :: {:ok, 0 | 1} | :noop | {:error, :not_found}
Populates visitor.device for visitor_id if and only if the current value is
empty (first-event-wins), mirroring maybe_set_geo/2. Device parsing is
synchronous, so this is called inline at ingest rather than via async enqueue.
Returns {:ok, count} (1 set, 0 already populated), :noop for empty
input, or {:error, :not_found} when the visitor does not exist.
@spec maybe_set_geo(Ecto.UUID.t(), map()) :: {:ok, 0 | 1} | :noop | {:error, :not_found}
Populates visitor.geo for visitor_id if and only if the current value is
empty. Used by async ingest enrichment (beacon, REST API) where we want
first-event-wins semantics without re-running identity resolution.
Implemented as a conditional UPDATE (SET geo = ? WHERE id = ? AND (geo = '{}'::jsonb OR geo IS NULL)) so the first-event-wins invariant
holds under concurrent click + beacon writes for the same visitor.
Returns {:ok, count} where count is 1 when the row was updated and
0 when another writer beat us to it, :noop when called with empty
input, or {:error, :not_found} when the visitor does not exist.
Gets the event timeline for a visitor, ordered by inserted_at desc.
Updates visitor attribution fields (extension point for GoodPartners).
Updates the lifecycle status of a visitor.