Skip to main content

Overview

watch() creates a real-time alert for a specific user. When that user’s risk score exceeds threshold, ChurnKit POSTs a signed payload to your webhook URL. The cooldown prevents repeat alerts from firing too frequently.

Signature

churn.watch(
  userId: string,
  options: WatchOptions
): Promise<WatchedUser>

WatchOptions

threshold
number
required
Fire the webhook when score >= threshold. Must be between 0 and 1.
webhook
string
required
HTTPS URL to POST the alert payload to. Must be a valid https:// URL.
cooldown
CooldownString
required
Minimum time between repeated alerts for this user. Format: positive integer + d (days) or h (hours). Examples: "24h", "7d", "1d".

WatchedUser (return value)

interface WatchedUser {
  userId: string
  threshold: number
  webhook: string
  cooldown: string
}

Examples

Basic watch

await churn.watch('user_123', {
  threshold: 0.7,
  webhook: 'https://yourapp.com/hooks/churn-alert',
  cooldown: '24h',
})

Watch all trial users

const trialUsers = await db.getTrialUsers()

await Promise.all(trialUsers.map((user) =>
  churn.watch(user.id, {
    threshold: 0.6,
    webhook: 'https://yourapp.com/hooks/trial-churn',
    cooldown: '7d',
  })
))

Webhook payload

When the alert fires, ChurnKit POSTs this JSON to your webhook URL:
{
  "event": "churn_risk_alert",
  "userId": "user_123",
  "score": 0.84,
  "tier": "high",
  "signals": ["no_login_7d", "support_spike"],
  "recommendation": "Reach out immediately.",
  "triggeredAt": "2024-06-01T14:22:00Z"
}
Always verify the webhook signature before processing the payload.

Handling the webhook in Next.js

// app/api/webhooks/churn/route.ts
import { verifyWebhookSignature } from '@vgpprasad91/churnkit-sdk'

export async function POST(req: Request) {
  const body = await req.text()
  const sig = req.headers.get('x-churnkit-signature') ?? ''

  const valid = await verifyWebhookSignature(body, sig, process.env.CHURNKIT_WEBHOOK_SECRET!)
  if (!valid) return Response.json({ error: 'Invalid signature' }, { status: 401 })

  const payload = JSON.parse(body)

  if (payload.event === 'churn_risk_alert' && payload.tier === 'high') {
    await notifyCustomerSuccess(payload.userId, payload)
  }

  return Response.json({ ok: true })
}

Errors

CodeWhen
VALIDATION_ERRORInvalid threshold, webhook URL, or cooldown format
UNAUTHORIZEDAPI key invalid
TIMEOUTRequest timed out