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

# Account takeover detection

> Detect account takeover attempts with velocity-based heuristics, automatic risk scoring, and real-time alerts.

The account takeover (ATO) detection API monitors login activity per subject and flags suspicious patterns in real time. It tracks failed login velocity in a rolling one-hour window, assigns a risk level based on configurable thresholds, and automatically creates alerts and [risk signals](/guides/risk-signals) when those thresholds are crossed.

<Info>
  ATO detection is part of the [Anti-Fraud Identity Firewall](/guides/risk-signals). Alerts and risk signals flow into the same pipeline as other fraud signals, so you can query and review them from the **Risk & Fraud > Signals** console page or the [list signals endpoint](/api-reference/risk-signals/list).
</Info>

## How it works

<Steps>
  <Step title="Send login events">
    Call `POST /v1/risk/ato/evaluate` each time a login event occurs — failed attempts, repeated failures, new device logins, or successful logins.
  </Step>

  <Step title="Velocity tracking">
    The platform increments (or resets) the failed login counter for the subject in a rolling one-hour window. Successful logins reset the window.
  </Step>

  <Step title="Risk level assignment">
    A risk level is derived from the current failed login count. The response includes the risk level, numeric score, and current counter value.
  </Step>

  <Step title="Automatic alerts and signals">
    When a subject crosses a threshold, the platform creates an alert and ingests a risk signal with `signal_type: "ato"`. You don't need a separate API call.
  </Step>

  <Step title="Review and respond">
    Query ATO profiles, list alerts, and review signals in the console or via the API to investigate and respond to threats.
  </Step>
</Steps>

## Prerequisites

* An active Truthlocks tenant with an API key
* Login event data from your authentication system (failed logins, successes, new device events)

## Evaluating a login event

Send a `POST` request to `/v1/risk/ato/evaluate` with the subject and event details:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.truthlocks.com/v1/risk/ato/evaluate \
    -H "X-API-Key: tl_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "subject_id": "user_abc123",
      "event_type": "login.failed",
      "ip_address": "198.51.100.42"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.truthlocks.com/v1/risk/ato/evaluate", {
    method: "POST",
    headers: {
      "X-API-Key": "tl_live_...",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      subject_id: "user_abc123",
      event_type: "login.failed",
      ip_address: "198.51.100.42",
    }),
  });
  const result = await response.json();
  ```

  ```python Python theme={null}
  import httpx

  resp = httpx.post(
      "https://api.truthlocks.com/v1/risk/ato/evaluate",
      headers={"X-API-Key": "tl_live_..."},
      json={
          "subject_id": "user_abc123",
          "event_type": "login.failed",
          "ip_address": "198.51.100.42",
      },
  )
  result = resp.json()
  ```
</CodeGroup>

The response tells you the current risk posture and whether an alert was triggered:

```json theme={null}
{
  "subject_id": "user_abc123",
  "subject_type": "user",
  "risk_level": "elevated",
  "risk_score": 50,
  "failed_login_count": 5,
  "alert": true,
  "alert_type": "velocity_exceeded",
  "signal_id": "9f3a1c2d-...",
  "event_type": "login.failed"
}
```

## Threshold rules

The platform assigns a risk level based on the number of failed logins within a one-hour rolling window:

| Failed logins (1 h window) | Risk level | Risk score | Alert type            |
| :------------------------- | :--------- | :--------- | :-------------------- |
| 0–4                        | `normal`   | 10         | —                     |
| 5–9                        | `elevated` | 50         | `velocity_exceeded`   |
| 10–19                      | `high`     | 70         | `velocity_exceeded`   |
| 20+                        | `critical` | 90         | `credential_stuffing` |

When a threshold is crossed:

1. An **alert** is created in the ATO alerts table
2. A **risk signal** is automatically ingested with `signal_type: "ato"` and the corresponding score
3. The response includes `alert: true`, the `alert_type`, and the `signal_id`

## Event types

| Event type              | Behavior                                                      |
| :---------------------- | :------------------------------------------------------------ |
| `login.failed`          | Increments the failed login counter                           |
| `login.failed.repeated` | Increments the failed login counter                           |
| `login.success`         | Resets the failed login counter for the subject               |
| `login.new_device`      | Evaluates the event and records the new device in the profile |

## Checking a subject's risk profile

Retrieve the current ATO risk profile for a subject without triggering a new evaluation:

```bash theme={null}
curl https://api.truthlocks.com/v1/risk/ato/profile/user_abc123 \
  -H "X-API-Key: tl_live_..."
```

The profile includes the current risk level, failed login count, and lists of known IP addresses and devices associated with the subject.

## Listing alerts

Query ATO alerts for your tenant, optionally filtered by subject:

```bash theme={null}
curl "https://api.truthlocks.com/v1/risk/ato/alerts?subject_id=user_abc123&limit=10" \
  -H "X-API-Key: tl_live_..."
```

Each alert includes the subject, alert type, risk level at the time of triggering, and a reference to the auto-ingested risk signal.

## Integration patterns

### Block or challenge on elevated risk

Use the `risk_level` from the evaluate response to decide whether to allow, challenge, or block a login:

```javascript theme={null}
const result = await evaluateATO(subjectId, "login.failed", ipAddress);

if (result.risk_level === "critical") {
  await lockAccount(subjectId);
  await notifySecurityTeam(result);
} else if (result.risk_level === "high") {
  await requireMFA(subjectId);
} else if (result.risk_level === "elevated") {
  await addCaptchaChallenge(subjectId);
}
```

### Combine with event normalization

ATO evaluation can complement the [event normalization pipeline](/guides/risk-signals#event-normalization). Use event normalization for broad risk scoring across all identity events, and ATO evaluation for focused account takeover monitoring with threshold-based alerts.

### Combine with velocity scoring

ATO detection focuses on login-based account takeover patterns with built-in threshold rules and alert management. [Velocity scoring](/guides/velocity-scoring) tracks any action type across multiple rolling time windows (1 m, 5 m, 1 h, 24 h) with weighted burst detection. Use both together for layered protection:

* **ATO detection** for focused login security — failed login velocity, credential stuffing alerts, subject risk profiles
* **Velocity scoring** for general abuse detection — API rate abuse, transaction flooding, registration spam, or any custom action type

Both produce risk signals in the same [unified pipeline](/guides/risk-signals), so you can query and review all signals from one place.

### Monitor via webhooks

Configure a [webhook endpoint](/guides/webhooks) to receive real-time notifications when ATO alerts are created, so your security team can respond without polling.

## What's next

* [Evaluate ATO risk API reference](/api-reference/risk/ato-evaluate)
* [Get ATO profile API reference](/api-reference/risk/ato-profile)
* [List ATO alerts API reference](/api-reference/risk/ato-alerts)
* [Risk signals guide](/guides/risk-signals)
* [Velocity and anomaly scoring guide](/guides/velocity-scoring)
* [Deepfake and impersonation detection guide](/guides/deepfake-detection)
