Skip to main content

Follow-Up Suggestions

The /suggestions page presents AI-generated recommendations for who to reach out to and why. Suggestions help you maintain relationships proactively rather than letting connections go cold.

How Suggestions Are Generated

The follow-up engine runs daily via Celery beat. It selects contacts across two pools with a combined budget of 5 suggestions per cycle.

Pool A — Active Relationships (3 slots)

Four independent triggers feed into Pool A. A contact only needs to match one:

TriggerConditionExample
Time-basedNo interaction in 90+ days, score > 0"Haven't talked to Alex in 3 months"
Event-basedDetectedEvent in last 7 days, confidence > 70%Bio change, tweet about fundraising
ScheduledPast the priority interval since last follow-upMedium priority set to 60 days, last follow-up was 65 days ago
BirthdayBirthday in next 3 daysHigh priority (1500), bypasses dormancy filter

Priority intervals are configurable per priority level in Settings (default: high=30d, medium=60d, low=180d).

Pool B — Dormant Revival (2 slots)

Surfaces contacts you've lost touch with but had meaningful past engagement. Three sub-triggers:

TriggerCondition
B1 — Deep dormantNo interaction in 365+ days, 2+ past interactions, score >= 3, history spans 30+ days
B2 — Mid dormantNo interaction in 180-365 days, 3+ past interactions, score >= 4
B3 — Event revivalDormant 180+ days but had a recent detected event (job change, etc.)

Cooldowns and Guards

All triggers respect these guards — including event-based triggers:

GuardRule
Interaction cooldownSkip if last interaction was within 14 days
Follow-up cooldownSkip if last follow-up suggestion was within 14 days
Already queuedSkip if a pending or snoozed suggestion already exists
ArchivedSkip archived contacts
2nd-tierSkip contacts labelled "2nd-tier"
No channelSkip contacts with no email, Telegram, or Twitter handle
Ghosting (3+)Skip if last 3+ consecutive interactions are all outbound with no reply
Ghosting (2)Reduce priority by 50% if last 2 consecutive interactions are outbound
Unread outboundSkip if last outbound Telegram message hasn't been read by recipient
Read no replyBoost priority +100 if last outbound was read but no inbound reply

Priority Scoring

Within each pool, candidates are ranked by priority score:

  • Interaction count — richer history = higher priority
  • Days since last contact — longer gap = higher priority
  • Event trigger bonus — +200 for event-based triggers (ensures timely events surface quickly)
  • Birthday bonus — +1500 for upcoming birthdays (highest priority)

Relationship Score

The relationship score (0-10) is computed from five weighted dimensions plus a tenure bonus:

DimensionWeightWhat it measures
Reciprocity30%Balance of inbound vs outbound messages
Recency25%How recently you interacted (exponential decay)
Frequency20%How often you interact
Breadth15%Number of platforms you communicate on
Tenure10%How long you've known the contact

Tenure bonus: 0-2 extra points based on relationship duration (caps at 2 years).

Extended decay: Interactions from 1-2 years ago still contribute at 0.05x weight; 2-5 years at 0.02x. This prevents long-term contacts from dropping to zero.

Event Classification

Bio changes detected during the daily Twitter profile poll create DetectedEvent records. When composing follow-up suggestions, the engine also fetches recent tweets on-demand via Bird CLI (cached in Redis for 12h) and classifies them with Claude into categories like job changes, fundraising, product launches, etc.

Tweet fetching and LLM classification are not run on the daily cron — they happen lazily only for contacts that actually need a suggestion.

AI Message Composer

Each suggestion includes an AI-drafted message generated by Claude. The draft takes into account:

  • The contact's profile, company, and bio information
  • The last 5 interactions (for tone analysis: formal vs casual)
  • Any detected events (job change, fundraising, etc.)
  • Recent Twitter activity (bio + last 5 tweets, fetched on-demand for every trigger type, 12h Redis cache)
  • The contact's preferred communication channel (based on most recent interaction platform)

When the last conversation is 90+ days old and a tweet from the last 30 days exists, the prompt instructs the LLM to anchor the message on the fresh Twitter signal rather than reopening the stale thread.

Contact Avatars

When a contact has an avatar available (from Google Contacts, Telegram, Twitter, or LinkedIn), it is displayed alongside the suggestion for quick visual identification.

Actions

For each suggestion, you can:

  • Edit and Send -- modify the AI-drafted message and send it through the appropriate channel.

  • Snooze -- defer the suggestion for a set period:

    • 2 weeks
    • 1 month
    • 3 months

    Snoozed suggestions are reactivated automatically via an hourly Celery beat task.

  • Dismiss -- remove the suggestion entirely. The contact will not be suggested again until a new trigger occurs.