---
title: "Fraud and abuse"
url: /trust/fraud
---

# Fraud and abuse

> Risk signals on every Customer account, scored together, a review a person decides, and restrictions that hold the risky part and keep the rest serving.

Every Customer account carries a risk score built from its signals; past a line, a person reviews it while restrictions hold only the risky part.

```mermaid title="A Customer account under risk"
stateDiagram-v2
  [*] --> clear: a sign-up
  clear --> watched: a signal
  watched --> in_review: score past review
  watched --> restricted: score past restrict
  in_review --> restricted: a person restricts
  restricted --> blocked: a person blocks
  in_review --> clear: cleared
  restricted --> clear: verified
```

Seen: in_review, restricted, blocked. Private, a step inside the run: clear, watched.

## What raises the score
A risk signal never refuses a call by itself.

Each one feeds the account's score, from 0 to 1, recomputed on each signal and kept with its reasons.

| Signal | What raises it |
| --- | --- |
| Card testing | Many small declined charges from one account, device or network, and Stripe's checks on each card |
| Free-call farming | Many sign-ups sharing a card, a device, a network or an email pattern, each taking the free calls or the trial |
| A leaked [Key](/reference/platform.md#key) | One found in public code, or one Key used from several countries within minutes |
| Scraping past a licence | Calls that walk every key in order, past what the licence `terms` allow |
| Refunds and disputes | Refunds or disputes above the product's own rate, or a dispute on every charge |
| A taken account | Sign-in from a new device and country, then a changed email, payout or Key |
| A runaway agent | An agent's spend far past its own history, even inside its mandate |
| Stripe's score | The risk score Stripe returns on each charge, read as one signal among the rest |

## Three accounts, three outcomes
The score decides who looks, and a person decides what is blocked. Compare how three accounts go.

| | An honest Customer, one odd signal | A card tester | A taken account |
| --- | --- | --- | --- |
| What happens | A new country raises the score past `risk.review`; it waits in a queue on the [Operator plane](/trust/planes.md) | Many small declines push it past `risk.restrict`; restriction holds the risky part at once | A changed payout after a new-device sign-in sends it to review, and a person restricts it |
| What keeps serving | Everything | Calls inside the account's usual spend | Calls inside the usual spend, while new Keys and payout changes wait |
| What answers 403 [`account_restricted`](/reference.md#account_restricted) | Nothing | New Keys, new purchases, raised ceilings and calls past the usual spend | The same |
| What it is asked | At most, to confirm its card | To confirm its card at the `verify` link | To confirm its identity |
| How it ends | A person clears it, within a day | Verified, and clear again, or blocked by a person | Verified by its owner, and clear again |

A block is only ever a person's decision: every Key revoked, each charge refunded by the published rule, the account's records kept. Each restriction and block sends a [statement of reasons](/trust/obligations.md#notices) and an appeal, which a second person decides.

## The lines you set
You write where review and restriction start; the file's [brakes](/trust.md) still cap calls and spend per Customer, whatever its score.

### Example
The [Dataset API](/patterns/dataset-api.md) gives one free allowance per card, reviews an account at 0.6 and restricts it at 0.85.

```ts title="definition.ts"
import { define, secret } from 'apis.dev'

/** Any company by its domain, with its headcount and sector, free to try once per card. */
export default define({
  name: 'companyfacts', domain: 'companyfacts.apis.dev',
  sources: {
    firms: { origin: 'https://firms.example.com', key: secret('FIRMS_KEY') },
  },
  Company: {
    domain:    { type: 'url!#',   from: 'firms', map: 'website' },
    employees: { type: 'number?', from: 'firms', map: 'headcount' },
    sector:    { type: 'string?', from: 'firms', map: 'industry' },
  },
  offer: {
    'Company.get': 0.002, 'Company.search': 0.005,
    free: { 'Company.get': 100 },
    credits: [10, 50, { pay: 200, get: 220 }],
  },
  operate: {
    brakes: { 'Company.search': { day: 5000 } },               // per Customer, whatever its score
    risk: { trials: 'card', review: 0.6, restrict: 0.85 },     // one free allowance per card
  },
})
```

A restricted account buying a new credit pack is asked to confirm its card, and its usual lookups keep running. A buyer's agent gets the same 403 and hands its `verify` link to its person.

```http title="Response"
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
X-Cost: price=0, currency="USD"
X-Request-Id: req_2f9b06d4c8e1a7357b0d

{
  "type": "https://companyfacts.apis.dev/docs/errors#account_restricted",
  "title": "Forbidden",
  "status": 403,
  "detail": "Under review: new purchases wait until the card is confirmed.",
  "code": "account_restricted",
  "instance": "/checkout",
  "verify": "https://companyfacts.apis.dev/dashboard/verify"
}
```

### One free allowance, and one queue
`risk.trials` grants one free trial or free-call allowance per card, per org or per sign-up.

Your team works one queue of reviews, each with the signals and calls behind its score, and every decision is kept. A review is `open`, then `cleared`, `restricted` or `blocked`, by `RiskReview.clear` or `RiskReview.block`. An appeal is `filed`, then `granted` or `denied`.