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

# Report Schema

> The attack/defend competitor report payload and the report-ready webhook

A competitor report has two layers: a **summary row** (counts, status, ids) returned in list views, and the full **`report_data`** attack/defend payload returned when you fetch a single report by id.

## Report object

`GET /api/public/competitor-monitoring/reports` returns these fields per report (the heavy `report_data` blob is omitted from the list). `GET .../reports/{reportID}` returns the same fields plus `search_params` and `report_data`.

| Field                | Type              | Description                                   |
| -------------------- | ----------------- | --------------------------------------------- |
| `id`                 | string            | Report id                                     |
| `run_id`             | string            | The run that produced this report             |
| `competitor_id`      | string            | The monitored competitor                      |
| `competitor_name`    | string            | Competitor company name                       |
| `persons_tracked`    | integer           | Competitor team members tracked in this run   |
| `interactions_found` | integer           | Total interactions captured                   |
| `atk_count`          | integer           | Number of attack accounts                     |
| `def_count`          | integer           | Number of defend accounts                     |
| `status`             | string            | `COMPLETED`                                   |
| `created_at`         | string (RFC 3339) | When the run started                          |
| `completed_at`       | string (RFC 3339) | When the report was written                   |
| `share_token`        | string            | Token for the shareable report view           |
| `search_params`      | object            | Run parameters (detail view only)             |
| `report_data`        | object            | Full attack/defend payload (detail view only) |

## `report_data`

The body splits into a `summary` header, the `atk` and `def` account lists (each an array of account signals, ranked by `signal_score` descending), and a `reps` drill-down.

### summary

| Field                | Type    | Description                                                     |
| -------------------- | ------- | --------------------------------------------------------------- |
| `persons_tracked`    | integer | Competitor team members tracked                                 |
| `interactions`       | integer | Total interactions folded into the report                       |
| `accounts_touched`   | integer | Distinct accounts the competitor engaged                        |
| `open_deals_at_risk` | integer | Accounts you own that have an open deal and competitor activity |
| `atk_accounts`       | integer | Count of attack accounts                                        |
| `def_accounts`       | integer | Count of defend accounts                                        |

### atk / def (account signal)

Each entry in `atk` and `def` is one account the competitor touched. `def` accounts are those you own (matched to a CRM company or owner); `atk` accounts are everything else.

| Field           | Type    | Description                                                             |
| --------------- | ------- | ----------------------------------------------------------------------- |
| `account_key`   | string  | Stable account identity (`domain:…`, else `company:…`, else `person:…`) |
| `company_name`  | string  | Resolved company name                                                   |
| `domain`        | string  | Resolved company domain                                                 |
| `owner_email`   | string  | CRM owner email (defend accounts)                                       |
| `owner_name`    | string  | CRM owner name                                                          |
| `signal_score`  | number  | Composite priority score (see [scoring](#how-the-signal-score-works))   |
| `distinct_reps` | integer | Distinct competitor reps that touched this account                      |
| `interactions`  | integer | Interactions on this account                                            |
| `comments`      | integer | How many were comments                                                  |
| `reactions`     | integer | How many were reactions/likes                                           |
| `we_own`        | boolean | True for defend accounts (CRM company or owner matched)                 |
| `has_open_deal` | boolean | An open CRM deal exists on the account                                  |
| `at_risk`       | boolean | `we_own` AND `has_open_deal`                                            |
| `targets`       | array   | People the competitor engaged at this account                           |

Each `targets` entry:

| Field                | Type    | Description                               |
| -------------------- | ------- | ----------------------------------------- |
| `target_linkedin_id` | string  | The engaged person                        |
| `target_name`        | string  | Their name                                |
| `we_know_target`     | boolean | Matches a CRM contact you already touched |

### reps

Per competitor rep, the interactions they made.

| Field             | Type   | Description                    |
| ----------------- | ------ | ------------------------------ |
| `rep_linkedin_id` | string | The competitor rep             |
| `rep_name`        | string | Their name                     |
| `rep_title`       | string | Their headline/title           |
| `role_bucket`     | string | Coarse GTM role classification |
| `interactions`    | array  | Their individual interactions  |

Each `interactions` entry:

| Field                | Type              | Description                                                           |
| -------------------- | ----------------- | --------------------------------------------------------------------- |
| `target_linkedin_id` | string            | Who they engaged                                                      |
| `target_name`        | string            | Their name                                                            |
| `company_name`       | string            | Resolved company                                                      |
| `domain`             | string            | Resolved domain                                                       |
| `interaction_type`   | string            | `comment`, `like`, `reaction`, … (comments are weighted more heavily) |
| `when`               | string (RFC 3339) | When it happened                                                      |
| `post_url`           | string            | Link to the post                                                      |
| `post_preview`       | string            | Snippet of the post                                                   |
| `owner_email`        | string            | CRM owner of the target's account                                     |
| `owner_name`         | string            | CRM owner name                                                        |
| `has_open_deal`      | boolean           | Open deal on the account                                              |
| `we_own`             | boolean           | You own the account                                                   |
| `we_know_target`     | boolean           | You already know this person                                          |

### How the signal score works

`signal_score` weights each interaction by its type (comments score higher than reactions), applies recency decay (interactions in the last 6 months count fully; older ones decay toward 0.7x past 24 months), and adds a bonus for the target's seniority. The per-account total is then multiplied by the number of distinct competitor reps that touched the account, so concentrated, multi-rep pushes rank highest.

## Example report

`GET /api/public/competitor-monitoring/reports/{reportID}`:

```json theme={null}
{
  "success": true,
  "data": {
    "id": "a7b8c9d0-1234-5678-9abc-def012345678",
    "run_id": "5f6e7d8c-9012-3456-789a-bcdef0123456",
    "competitor_id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
    "competitor_name": "Globex Inc",
    "persons_tracked": 12,
    "interactions_found": 47,
    "atk_count": 8,
    "def_count": 3,
    "status": "COMPLETED",
    "created_at": "2026-06-29T09:00:00Z",
    "completed_at": "2026-06-29T09:06:12Z",
    "share_token": "9b2f1e7a-0c44-4d2a-9f3b-7e1c2d4a5b6c",
    "search_params": {},
    "report_data": {
      "summary": {
        "persons_tracked": 12,
        "interactions": 47,
        "accounts_touched": 11,
        "open_deals_at_risk": 2,
        "atk_accounts": 8,
        "def_accounts": 3
      },
      "atk": [
        {
          "account_key": "domain:acme.com",
          "company_name": "Acme Corp",
          "domain": "acme.com",
          "signal_score": 18.4,
          "distinct_reps": 3,
          "interactions": 6,
          "comments": 2,
          "reactions": 4,
          "we_own": false,
          "has_open_deal": false,
          "at_risk": false,
          "targets": [
            { "target_linkedin_id": "jane-smith", "target_name": "Jane Smith", "we_know_target": true },
            { "target_linkedin_id": "raj-patel", "target_name": "Raj Patel", "we_know_target": false }
          ]
        }
      ],
      "def": [
        {
          "account_key": "domain:initech.com",
          "company_name": "Initech",
          "domain": "initech.com",
          "owner_email": "alice@yourco.com",
          "owner_name": "Alice Martin",
          "signal_score": 9.7,
          "distinct_reps": 1,
          "interactions": 2,
          "comments": 1,
          "reactions": 1,
          "we_own": true,
          "has_open_deal": true,
          "at_risk": true,
          "targets": [
            { "target_linkedin_id": "peter-g", "target_name": "Peter Gibbons", "we_know_target": true }
          ]
        }
      ],
      "reps": [
        {
          "rep_linkedin_id": "bob-seller",
          "rep_name": "Bob Seller",
          "rep_title": "Account Executive at Globex",
          "role_bucket": "sales",
          "interactions": [
            {
              "target_linkedin_id": "jane-smith",
              "target_name": "Jane Smith",
              "company_name": "Acme Corp",
              "domain": "acme.com",
              "interaction_type": "comment",
              "when": "2026-06-24T14:12:00Z",
              "post_url": "https://www.linkedin.com/feed/update/urn:li:activity:123",
              "post_preview": "Excited to share our new pricing...",
              "has_open_deal": false,
              "we_own": false,
              "we_know_target": true
            }
          ]
        }
      ]
    }
  }
}
```

## Report-ready webhook

If your workspace has webhook delivery enabled, Clustr POSTs a `competitor_monitoring.report_ready` event to your configured URL when a run completes. The payload is a summary plus a deep link; fetch the full report by id for the detail.

```json theme={null}
{
  "event": "competitor_monitoring.report_ready",
  "tenant_id": "t_abc123",
  "competitor_id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
  "competitor_name": "Globex Inc",
  "report_id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "persons_tracked": 12,
  "interactions_found": 47,
  "atk_count": 8,
  "def_count": 3,
  "report_link": "https://app.clustr-ai.com/competitor-monitoring/reports/a7b8c9d0-1234-5678-9abc-def012345678"
}
```

| Field                | Type    | Description                                 |
| -------------------- | ------- | ------------------------------------------- |
| `event`              | string  | Always `competitor_monitoring.report_ready` |
| `tenant_id`          | string  | Your workspace id                           |
| `competitor_id`      | string  | The monitored competitor                    |
| `competitor_name`    | string  | Competitor company name                     |
| `report_id`          | string  | Fetch the full report with this id          |
| `persons_tracked`    | integer | Competitor team members tracked             |
| `interactions_found` | integer | Total interactions captured                 |
| `atk_count`          | integer | Attack account count                        |
| `def_count`          | integer | Defend account count                        |
| `report_link`        | string  | Deep link to the full report in the app     |

The matching **email** notification carries the same attack/defend counts, the interactions and people-tracked totals, and a "View Full Report" button to `report_link`.
