> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowsbuilt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# watch()

> Register a webhook that fires when a user's churn risk crosses a threshold.

## 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

```typescript theme={null}
churn.watch(
  userId: string,
  options: WatchOptions
): Promise<WatchedUser>
```

## WatchOptions

<ParamField path="threshold" type="number" required>
  Fire the webhook when `score >= threshold`. Must be between 0 and 1.
</ParamField>

<ParamField path="webhook" type="string" required>
  HTTPS URL to POST the alert payload to. Must be a valid `https://` URL.
</ParamField>

<ParamField path="cooldown" type="CooldownString" required>
  Minimum time between repeated alerts for this user. Format: positive integer + `d` (days) or `h` (hours). Examples: `"24h"`, `"7d"`, `"1d"`.
</ParamField>

## WatchedUser (return value)

```typescript theme={null}
interface WatchedUser {
  userId: string
  threshold: number
  webhook: string
  cooldown: string
}
```

## Examples

### Basic watch

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

### Watch all trial users

```typescript theme={null}
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:

```json theme={null}
{
  "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](/churnkit/webhooks/verify-signature) before processing the payload.

### Handling the webhook in Next.js

```typescript theme={null}
// 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

| Code               | When                                                     |
| ------------------ | -------------------------------------------------------- |
| `VALIDATION_ERROR` | Invalid `threshold`, `webhook` URL, or `cooldown` format |
| `UNAUTHORIZED`     | API key invalid                                          |
| `TIMEOUT`          | Request timed out                                        |
