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=20Statuses: pending, fired, cancelled, failed, expired
Cancelling a trigger
POST /outreach/:id/cancelOnly 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:
- Fires pending triggers — finds all triggers where
scheduled_at ≤ now, runs pre-delivery checks, generates a message (if needed), and delivers it - Detects inactivity — finds contacts who haven't messaged in
inactivity_daysand creates new inactivity triggers (capped atmax_inactivity_attempts) - 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.