Skip to content

Measure Instrumentation Spec: Brainshelf Resurface

Scenario

Before Sprint 8 began, Chloe B. wrote the instrumentation spec for the Resurface feature to ensure every measurable interaction . opt-in, email sent, email opened, item clicked, cadence changed, unsubscribe, and digest skipped . was tracked consistently across the client and server. The spec was written early so that Alex R. and Jess T. could instrument events during the build rather than retrofitting analytics after ship, which the team had learned from a prior feature launch was error-prone and created data gaps in the first week of measurement.

Source Notes:

  • Amplitude, “Event Tracking Best Practices” (amplitude.com/blog/event-tracking-best-practices) . the event naming conventions and property taxonomy used in this spec; Amplitude’s guidance on using snake_case naming, consistent property types, and separating user properties from event properties informed the structure.
  • Segment, “Analytics Academy: Tracking Plan” (segment.com/academy/collecting-data/naming-conventions-for-clean-data/) . the tracking plan methodology that influenced the event inventory format; Segment’s emphasis on a centralized tracking plan document as the single source of truth for all analytics events shaped the document’s role in the development workflow.
  • GDPR Article 6, “Lawfulness of Processing” (gdpr-info.eu/art-6-gdpr/) . the legal basis for processing analytics data in the context of the Resurface feature; the consent and legitimate interest considerations referenced in the PII & Privacy section are drawn from GDPR Article 6.

Prompt

/instrumentation-spec
resurface analytics events for amplitude. need the full spec before
sprint 8 starts so alex and jess can instrument during the build.
events:
1. resurface_opt_in . user enables the digest
2. resurface_digest_sent . server sends a digest email
3. resurface_digest_opened . user opens the email (caveat: apple MPP)
4. resurface_item_clicked . user clicks an item in the digest
5. resurface_unsubscribe . user unsubscribes
6. resurface_cadence_changed . user changes frequency
7. resurface_digest_skipped . server skips sending (no qualifying items,
exclusion window exhausted, etc.)
also need user properties: digest_enabled, digest_cadence, digest_timezone.
keep it tight. chloe wants to review before sprint planning on monday.

Output

Instrumentation Spec: Resurface Digest

Overview

Feature: Resurface . daily morning email digest of topic-matched saved items

Analytics Goals:

  1. Can we measure the causal effect of the Resurface digest on 7-day return rate? (A/B experiment primary metric)
  2. Are users engaging with the digest content? (Email CTR, item-level click distribution)
  3. Is the digest creating negative value? (Unsubscribe rate, guardrail monitoring)
  4. What is the opt-in funnel conversion rate? (Card viewed → opted in)
  5. How often does the system skip digest generation, and why? (Operational health)

Analytics Platform: Amplitude

Naming Convention: resurface_ prefix, snake_case: resurface_{action} (e.g., resurface_opt_in)

Event Inventory

resurface_opt_in

FieldValue
Event Nameresurface_opt_in
TriggerUser taps “Enable Digest” on the home screen card OR toggles the Resurface Digest setting on in Settings > Notifications
DescriptionRecords the moment a user enables the Resurface digest. Fires once per opt-in action; if a user unsubscribes and re-subscribes, a new opt-in event fires.

Properties:

PropertyTypeRequiredDescriptionExample
sourcestringYesWhere the opt-in was triggered"home_card" or "settings"
timezonestringYesUser’s IANA timezone at opt-in time"America/New_York"
library_sizenumberYesNumber of saved items at opt-in time127
experiment_variantstringYesA/B experiment variant"treatment"

resurface_opt_in_card_viewed

FieldValue
Event Nameresurface_opt_in_card_viewed
TriggerThe Resurface opt-in card renders on the user’s home screen (viewport visible for ≥1 second)
DescriptionRecords that the opt-in card was displayed to the user. Used to calculate the card-to-opt-in conversion rate.

Properties:

PropertyTypeRequiredDescriptionExample
experiment_variantstringYesA/B experiment variant"treatment"
view_countnumberYesHow many times this user has seen the card (cumulative)1

resurface_opt_in_card_dismissed

FieldValue
Event Nameresurface_opt_in_card_dismissed
TriggerUser taps the dismiss (“X”) button on the opt-in card
DescriptionRecords that the user dismissed the opt-in prompt without opting in.

Properties:

PropertyTypeRequiredDescriptionExample
dismiss_countnumberYesCumulative dismiss count for this user (1 = first dismiss, 2 = second/final dismiss)1
experiment_variantstringYesA/B experiment variant"treatment"

resurface_digest_sent

FieldValue
Event Nameresurface_digest_sent
TriggerThe server successfully sends a digest email via the Resend API (HTTP 200 response from Resend)
DescriptionRecords each successful digest email send. Fired server-side by the cron job.

Properties:

PropertyTypeRequiredDescriptionExample
digest_idstringYesUnique identifier for this digest instance"dg_2026-03-10_usr_abc123"
item_countnumberYesNumber of items included in the digest4
itemsarrayYesArray of item IDs included in the digest["item_1", "item_2", "item_3", "item_4"]
cadencestringYesUser’s cadence setting at send time"daily" or "3x_week"
scheduled_time_utcstringYesScheduled send time in UTC"2026-03-10T12:30:00Z"
actual_send_time_utcstringYesActual send time in UTC"2026-03-10T12:30:04Z"
timezonestringYesUser’s IANA timezone"America/New_York"

resurface_digest_opened

FieldValue
Event Nameresurface_digest_opened
TriggerResend webhook delivers an “opened” event for a digest email
DescriptionRecords that the user (or their email client) opened the digest email. Caveat: Apple Mail Privacy Protection pre-loads tracking pixels, inflating open rates for Apple Mail users. This event is logged for completeness but is NOT used as a primary or secondary metric.

Properties:

PropertyTypeRequiredDescriptionExample
digest_idstringYesUnique identifier for the digest"dg_2026-03-10_usr_abc123"
email_clientstringNoEmail client if identifiable from Resend metadata"gmail" or "apple_mail"

resurface_item_clicked

FieldValue
Event Nameresurface_item_clicked
TriggerUser clicks an item link in the digest email, hitting the /r/{digest_id}/{item_id} redirect endpoint
DescriptionRecords each item click from the digest. This is the core engagement event and the basis for the email CTR metric. Fired server-side by the click redirect endpoint.

Properties:

PropertyTypeRequiredDescriptionExample
digest_idstringYesUnique identifier for the digest"dg_2026-03-10_usr_abc123"
item_idstringYesSaved item identifier"item_42"
item_positionnumberYesPosition of the item in the digest (1-indexed)2
item_topic_tagstringYesTopic tag displayed in the digest for this item"product-management"
relevance_scorenumberYesTF-IDF cosine similarity score for this item0.34
destination_urlstringYesThe original saved URL the user is redirected to"https://example.com/article"

resurface_unsubscribe

FieldValue
Event Nameresurface_unsubscribe
TriggerUser clicks the “Unsubscribe” link in the digest email footer, hitting the /digest/unsubscribe/{user_token} endpoint
DescriptionRecords that the user unsubscribed from the Resurface digest.

Properties:

PropertyTypeRequiredDescriptionExample
sourcestringYesWhere the unsubscribe was triggered"email_footer" or "settings"
days_since_opt_innumberYesDays between opt-in and unsubscribe14
digests_receivednumberYesTotal digest emails the user received before unsubscribing12
total_items_clickednumberYesTotal items clicked across all digests before unsubscribing5

resurface_cadence_changed

FieldValue
Event Nameresurface_cadence_changed
TriggerUser changes their digest cadence (from daily to 3x/week or vice versa) via email footer link or Settings
DescriptionRecords cadence preference changes.

Properties:

PropertyTypeRequiredDescriptionExample
sourcestringYesWhere the change was triggered"email_footer" or "settings"
previous_cadencestringYesCadence before the change"daily"
new_cadencestringYesCadence after the change"3x_week"

resurface_digest_skipped

FieldValue
Event Nameresurface_digest_skipped
TriggerThe cron job determines that a digest should not be sent for a user on a given day
DescriptionRecords each skipped digest with the reason. Fired server-side. Used for operational monitoring (not user-facing metrics).

Properties:

PropertyTypeRequiredDescriptionExample
skip_reasonstringYesWhy the digest was skipped"no_qualifying_items", "exclusion_window_exhausted", "cadence_not_today", "send_failed_after_retries", "user_unsubscribed_mid_batch"
items_evaluatednumberNoNumber of candidate items scored47
items_above_thresholdnumberNoNumber of items that passed the relevance threshold0

User Properties

PropertyTypeDescriptionSet WhenExample
digest_enabledbooleanWhether the user has opted in to the Resurface digestSet to true on opt-in; set to false on unsubscribe or hard bounce disabletrue
digest_cadencestringUser’s selected delivery frequencySet on opt-in (default "daily"); updated on cadence change"daily" or "3x_week"
digest_timezonestringUser’s IANA timezone for digest deliverySet on opt-in from device settings; updated on app open if device timezone changes"America/New_York"
digest_opted_in_atstringISO 8601 timestamp of the user’s most recent opt-inSet on each opt-in (including re-subscribes)"2026-03-10T14:22:00Z"
experiment_variantstringA/B experiment variant assignmentSet when the experiment SDK initializes"control" or "treatment"

PII & Privacy Considerations

PII Properties

PropertyPII TypeHandling
destination_urlPotentially PII (URL may contain user-specific query parameters)Log the base URL only; strip query parameters before storing in Amplitude
email_clientIndirect PII (identifies user’s email service)Acceptable to store; no direct identification
  • Analytics events for the Resurface feature are processed under the legitimate interest basis (GDPR Article 6(1)(f)) as product improvement analytics
  • No additional consent collection is required beyond the existing Brainshelf privacy policy, which covers product analytics
  • The opt-in event itself is consent for email delivery, not consent for analytics; analytics consent is covered by the app-level privacy policy

Data Retention

  • Event data retained in Amplitude for 12 months (standard Brainshelf retention policy)
  • Digest send logs in PostgreSQL retained for 90 days, then archived

Implementation Notes

SDK/Integration

  • Platform: Server-side (Node.js) for digest_sent, digest_opened (via webhook), item_clicked, unsubscribe, digest_skipped; client-side (web/mobile) for opt_in, opt_in_card_viewed, opt_in_card_dismissed, cadence_changed
  • SDK: Amplitude JavaScript SDK (client), Amplitude Node.js SDK (server)
  • Initialization: Client-side SDK must be initialized before the home screen renders to ensure variant assignment is available for the opt-in card display logic

Event Timing

  • Client-side events fire immediately on user interaction (no batching delay)
  • Server-side events (digest_sent, digest_skipped) fire synchronously within the cron job after each user’s digest is processed
  • item_clicked fires synchronously in the click redirect endpoint before the 302 redirect is issued (must not block the redirect; use non-blocking async send with local fallback queue)

Testing Checklist

Event Validation

  • resurface_opt_in: Enable digest from home card; verify event fires with source="home_card", correct timezone, and library_size
  • resurface_opt_in: Enable digest from Settings; verify event fires with source="settings"
  • resurface_opt_in_card_viewed: Open home screen as treatment user; verify event fires after card is visible for ≥1 second
  • resurface_opt_in_card_dismissed: Tap dismiss on home card; verify event fires with dismiss_count=1
  • resurface_digest_sent: Trigger a manual digest send for a test user; verify event fires with correct digest_id, item_count, items array, and timezone
  • resurface_item_clicked: Click an item in a test digest email; verify event fires with correct digest_id, item_id, item_position, relevance_score, and destination_url
  • resurface_unsubscribe: Click unsubscribe in a test digest email; verify event fires with correct source and days_since_opt_in
  • resurface_cadence_changed: Change cadence from daily to 3x/week via email footer; verify event fires with correct previous/new cadence
  • resurface_digest_skipped: Trigger skip condition (e.g., empty library for test user); verify event fires with correct skip_reason

Property Validation

  • Verify item_position is a number between 1 and 5
  • Verify relevance_score is a number between 0 and 1
  • Verify cadence values are only "daily" or "3x_week" (no other strings)
  • Verify timezone is a valid IANA timezone string
  • Verify destination_url has query parameters stripped

Edge Cases

  • Verify resurface_item_clicked fires correctly when the user clicks multiple items in the same digest (each click is a separate event)
  • Verify resurface_digest_sent does NOT fire when the Resend API returns an error (only fires on HTTP 200)
  • Verify events do not fire for control group users (no opt-in card viewed, no digest sent, no item clicked)
  • Verify resurface_opt_in fires only once per tap (debounce prevents double-fire if user taps “Enable Digest” twice quickly)

Debug Tools

  • Amplitude Event Explorer: filter by user_id and event name to verify individual events in real time
  • Resend webhook logs: verify that digest_opened events match Resend’s delivery webhook payloads
  • Server logs (Datadog): search for digest_id to trace the full lifecycle of a single digest from generation to click