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

# Send results to your SIEM or LMS

> Post each call result to your own system with a campaign webhook URL, with the full payload, when it fires, and how to protect an unsigned endpoint.

A campaign can post each call result to a URL you own. The wizard says: "When a person is compromised or resists, we POST the result here so your SIEM, SOAR or training platform can act on it. HTTPS only." Use it to open a ticket, enrol a person in training, or keep your own record.

> [!WARNING]
>
> Requests are **not signed**. There is no signature header to verify. Protect the endpoint yourself: put a secret in the URL path or query, or accept requests only from DeepBlocker's addresses.

## Before you start

* An HTTPS endpoint on a public address that accepts a JSON POST and replies within 10 seconds.
* A campaign on its last wizard step, or a template you want to carry the URL.

## Steps

**1. Open the advanced field**

On the **Authorise and launch** step, under **Name this campaign**, click **Advanced: send results to a webhook**. A **Webhook URL** field appears.

**2. Enter the URL**

Paste the address, for example `https://your-siem-or-lms.example.com/hook?key=...`. It must start with `https://` and resolve to a public address. A private or internal address is refused silently and nothing is sent.

**3. Launch**

Record the authorisation and click **Launch**. The URL is saved with the campaign, and with any template made from it. See [Authorise and launch](https://docs.deepblocker.ai/vishing/campaigns/authorise.md).

## When it fires

| Rule | Detail |
| - | - |
| Which calls | Calls that ended **Failed** or **Passed**, that is, a person answered and talked |
| Which calls do not fire | No answer, voicemail, call failed. A **Reported** result does not fire |
| How many times | Once per call, on the first delivery of the call's result |
| When | After the call ends and the live detector verdict, if any, is on the record. A verdict that lands later is not re-sent |
| Timeout | 10 seconds |
| Retries | None. A failed delivery is logged on our side and not retried |
| Method and headers | `POST`, `Content-Type: application/json`, `User-Agent: DeepBlocker-RedTeam/1.0` |

## The payload

The body is one JSON object. Fields marked optional are left out when there is nothing to send.

| Field | Type | What it holds |
| - | - | - |
| `event` | string | Always `redteam.call.result` |
| `campaignId` | string | The campaign |
| `campaignName` | string, optional | The campaign's name |
| `contactId` | string, optional | The person's id on your People list |
| `contactName` | string, optional | The person's name |
| `outcome` | string | `compromised` or `resisted` |
| `riskScore` | number, optional | The analysis risk score, 0 to 100 |
| `informationDisclosed` | array of strings, optional | What the person gave away |
| `occurredAt` | string | When the call ended, ISO 8601 |
| `defenseVerdict` | object | Our detector's result on this call, below |
| `campaignSummary` | object | The campaign's numbers at send time, below |

### `defenseVerdict`

| Field | Type | What it holds |
| - | - | - |
| `state` | string | `caught`, `missed` or `not_scored`. See [The "detector caught" column](https://docs.deepblocker.ai/vishing/results/detector.md) |
| `fakeProbability` | number, optional | 0 to 1, how synthetic the voice scored. Present when scored |
| `threshold` | number, optional | 0 to 1, the score at or above which the detector blocks. Present when scored |
| `modelName` | string, optional | The detector model |
| `source` | string, optional | `vapi` or `livekit` for a recording scored after the call, `live` for the live detector on a protected line |

### `campaignSummary`

Counts are per person, with retries collapsed. They move as more calls finish, so keep the latest event per campaign rather than adding events up.

| Field | Type | What it holds |
| - | - | - |
| `reached` | number | People who answered and talked |
| `gaveAway` | number | People who gave something away |
| `reported` | number | People who reported the call |
| `blocked` | number | Calls the detector would have blocked |
| `residual` | number | People who failed on a call the detector missed |
| `scored` | number | Calls with a detector verdict |
| `unscoredFailures` | number | Failed calls with no verdict yet |
| `staffFailurePct` | number | `gaveAway` out of `reached`, as a percentage |
| `detectorCatchPct` | number or null | Of the failed calls with a score, the share the detector would have blocked. `null` until one failed call is scored |
| `residualPct` | number | `residual` out of `reached` |
| `sentence` | string | The one-line summary the console shows |

### Example

```json redteam.call.result theme={null}
{
  "event": "redteam.call.result",
  "campaignId": "c_8f2a1b",
  "campaignName": "Finance desk, November",
  "contactId": "ct_41d9",
  "contactName": "Sam Patel",
  "outcome": "compromised",
  "riskScore": 82,
  "informationDisclosed": ["Employee ID", "Manager's name"],
  "occurredAt": "2026-11-10T10:42:17.000Z",
  "defenseVerdict": {
    "state": "caught",
    "fakeProbability": 0.93,
    "threshold": 0.8,
    "modelName": "v7",
    "source": "livekit"
  },
  "campaignSummary": {
    "reached": 12,
    "gaveAway": 3,
    "reported": 1,
    "blocked": 2,
    "residual": 1,
    "scored": 11,
    "unscoredFailures": 0,
    "staffFailurePct": 25,
    "detectorCatchPct": 67,
    "residualPct": 8,
    "sentence": "25% of staff gave something away. 8% reported the call. Our detector would have blocked 67% of those calls before a person picked up. Residual exposure: 8%."
  }
}
```

## What happens next

Your endpoint receives one event per answered call as the campaign runs. Reply with any 2xx status. There is nothing to acknowledge beyond that.

> [!TIP]
>
> Names are sent in `contactName` whatever your **Show names in reports** setting is. If your endpoint should not hold names, leave `contactName` unused on your side.

## Problems

> [accordion] Nothing arrives
>
> Check the URL starts with `https://` and points at a public address. Check your endpoint answers within 10 seconds. Only calls that ended **Failed** or **Passed** fire.

> [accordion] A call fired once and never again
>
> That is by design. Each call fires once. A detector verdict that lands later is carried in the next event's `campaignSummary`.

> [accordion] Someone could post fake results to my endpoint
>
> Yes, because the request is not signed. Put a secret in the URL, restrict by source, and treat the data as a trigger to look at the console, not as the record itself.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.