vlozi/docs

Proactive outreach

Proactive outreach

How CI automatically schedules messages for inactivity, milestones, and follow-ups.

Contact Intelligence can send proactive messages to contacts — not just reply to them. This covers re-engagement after silence, milestone celebrations, and scheduled follow-up messages. All messages are personalised using the contact's memory before sending.

Trigger types

Type When it fires Example message
inactivity Contact inactive for N days (configurable) "Hey! It's been a while — how's Bruno doing?"
milestone 7-day active streak "One week of chatting every day! I really enjoy our conversations 😊"
milestone 100th message "We've had quite the journey! Thanks for being here 🎉"
scheduled A specific time you set manually "Good luck with your presentation today! You've got this."

Messages are generated by an LLM using the contact's top memories — so the bot references real things the customer shared, not generic filler.

Outreach config

Configure outreach rules in Contact Intelligence → Outreach Settings:

Setting Default What it controls
Enabled On Master switch — turn off to pause all outreach
Inactivity threshold 3 days Days of silence before an inactivity trigger is created
Max inactivity attempts 2 How many re-engagement messages per inactive period
Min gap between messages 3 days Prevents messages too close together
Max per day 3 Hard cap: max outreach messages per contact per day
Quiet hours start 22:00 No messages sent after this time
Quiet hours end 08:00 No messages sent before this time
Timezone Asia/Kolkata Timezone for quiet hours calculation
PATCH /outreach/config
Content-Type: application/json
 
{
  "enabled": true,
  "inactivity_days": 5,
  "max_inactivity_attempts": 3,
  "min_gap_days": 2,
  "quiet_hours_start": 21,
  "quiet_hours_end": 9,
  "timezone": "Asia/Kolkata"
}

Pre-delivery checks

Before any trigger fires, CI runs a set of checks. If any fail, the trigger is cancelled:

Check What it verifies
Contact exists Contact record is present and not deleted
Outreach enabled outreach_disabled is false on the contact
Stage not dormant Contacts inactive 30+ days receive no outreach
Daily rate limit Contact has not already received max messages today
Gap since last message Minimum gap since last outreach has passed
Quiet hours Message is scheduled outside the configured quiet window

If the contact is in quiet hours, the trigger is rescheduled to the next allowed window — not cancelled.

Scheduling a custom outreach

To schedule a specific message for a specific contact:

POST /outreach
Content-Type: application/json
 
{
  "contact_id": "ct_01j...",
  "trigger_type": "scheduled",
  "scheduled_at": "2026-07-01T08:00:00Z",
  "message": "Good luck with the board meeting today, Priya!"
}

Leave message blank to have the LLM generate a personalised message based on the contact's memories:

POST /outreach
Content-Type: application/json
 
{
  "contact_id": "ct_01j...",
  "trigger_type": "scheduled",
  "scheduled_at": "2026-07-01T08:00:00Z",
  "context": { "event": "job_interview", "notes": "Priya mentioned she has an interview on July 1st" }
}

Response:

{
  "trigger": {
    "id": "ot_01j...",
    "contact_id": "ct_01j...",
    "trigger_type": "scheduled",
    "status": "pending",
    "scheduled_at": "2026-07-01T08:00:00Z"
  }
}

Listing triggers

GET /outreach?status=pending&contact_id=ct_01j...&limit=20

Statuses: pending, fired, cancelled, failed, expired

Cancelling a trigger

POST /outreach/:id/cancel

Only pending triggers can be cancelled. A trigger that has already fired cannot be undone.

Stopping outreach for one contact

If a customer asks not to receive proactive messages, set their contact to opt-out:

Dashboard: Contact profile → Stop outreach

This sets outreach_disabled = true and is honoured for all future trigger checks. It does not cancel pending triggers already in the queue — cancel those separately.

How the scanner works

CI runs a background scanner every 6 hours that:

  1. Fires pending triggers — finds all triggers where scheduled_at ≤ now, runs pre-delivery checks, generates a message (if needed), and delivers it
  2. Detects inactivity — finds contacts who haven't messaged in inactivity_days and creates new inactivity triggers (capped at max_inactivity_attempts)
  3. Detects milestones — finds contacts who hit a 7-day active streak or their 100th message and creates a milestone trigger

The scanner also respects dormant contacts — no outreach is ever sent if the contact has been inactive for 30+ days.

Contact Intelligence · Proactive outreachEdit on GitHub