---
title: "Monitors"
description: "Watch any professional profile or company for changes and receive signed webhooks when something updates."
---

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

# Monitors

Subscribe to any profile or company URL. When a change is detected (job title, company, headline, headcount), Kooperativa sends a signed POST to your `webhook_url`.

:::tip
**Free**: no credits charged for creating, listing, or deleting monitors, nor for webhook deliveries. You only pay for the initial enrichment calls to get profile IDs.
:::

## How it works

1. Create a monitor with a profile or company URL and your webhook endpoint
2. Kooperativa detects changes as profiles are ingested from the data lake
3. A signed webhook fires immediately. No polling needed.

## Available events

### Person events

| Event | Fires when |
|---|---|
| `person.job_changed` | Current company changes |
| `person.title_changed` | Job title changes (same company) |
| `person.headline_changed` | Profile headline changes |
| `person.location_changed` | City or country changes |
| `person.skills_changed` | Skills list changes |

### Company events

| Event | Fires when |
|---|---|
| `company.staff_changed` | Employee count changes |
| `company.description_changed` | Company description changes |

## Webhook payload

```json
{
  "id": "c1c882c8-f172-4c00-8efc-be6fa9e3add4",
  "event": "person.job_changed",
  "monitor_id": "45e0bba8-196d-4f5e-aecc-52332ca832e2",
  "timestamp": "1752918000",
  "diff": {
"old_company": "Microsoft",
"new_company": "OpenAI",
"old_title": "CEO",
"new_title": "Board Member"
  }
}
```

## Signature verification

Every delivery includes these headers:

| Header | Description |
|---|---|
| `X-Kooperativa-Request-Id` | Unique delivery UUID |
| `X-Kooperativa-Event` | Event type |
| `X-Kooperativa-Timestamp` | Unix timestamp (seconds) |
| `X-Kooperativa-Signature` | HMAC-SHA256 of `v0:{timestamp}:{body}` |

```js title="Verify webhook signature"
import crypto from "crypto";

function verifyWebhook(req, secret) {
  const timestamp = req.headers["x-kooperativa-timestamp"];
  const signature = req.headers["x-kooperativa-signature"];
  const expected = crypto
.createHmac("sha256", secret)
.update(`v0:${timestamp}:${JSON.stringify(req.body)}`)
.digest("hex");
  return expected === signature;
}
```

:::caution
Always verify the webhook signature before processing the payload. Reject any request where the signature does not match, as it may be forged or replayed.
:::

## Retry policy

If your endpoint doesn't respond with `2xx` within 30 seconds, Kooperativa retries up to 5 times:

| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |

:::tip
Make your webhook handler **idempotent**. It may receive the same delivery more than once. Use the `X-Kooperativa-Request-Id` header to deduplicate.
:::

Source: https://docs.kooperativa.io/monitors/overview/index.mdx
