# Authentication (/docs/authentication) Every request to the AbuseZero API needs a secret key in the `Authorization` header. ```http Authorization: Bearer az_live_8d1f0c3b5e2a ``` ## Key types [#key-types] :::warning Never ship a secret key (`az_live_` or `az_test_`) to the browser or a mobile app. Anyone with the key can run checks on your account. ::: ## Rotating keys [#rotating-keys] ### Create a new key [#create-a-new-key] In the dashboard, open **Settings → API keys** and create a second key. ### Deploy it [#deploy-it] Update `ABUSEZERO_API_KEY` in every environment and redeploy. ### Revoke the old key [#revoke-the-old-key] Requests using a revoked key fail with `401 unauthorized` immediately. # Benefit history (/docs/concepts/benefit-history) Benefit history is the record of every free trial, credit grant, coupon and referral bonus you have handed out. It's what lets AbuseZero recognize the same person coming back with a new email. ## Linked identities [#linked-identities] When you record a benefit, AbuseZero links every identifier you send. If any of them show up again, the new check sees the earlier claim. ![How identities are linked across claims](/images/placeholder.png) ## Recording claims [#recording-claims] Record a benefit right after you grant it, and pass the `checkId` that approved it so the two are connected. ```ts title="lib/billing.ts" await abusezero.benefits.record({ identity: { email, deviceId, paymentFingerprint }, type: 'credit', amount: 25, checkId: check.id, // [!code ++] }); ``` ## Where data lives [#where-data-lives] Benefit records are stored per environment, so sandbox claims never affect production checks. ```files abusezero-account ├── production │ ├── checks │ ├── benefits │ └── rules └── sandbox ├── checks ├── benefits └── rules ``` ## Retention [#retention] Benefit history is kept for 90 days. Benefit history is kept for 2 years. Retention is configurable, up to 7 years. # Risk checks (/docs/concepts/risk-checks) A risk check takes an identity and an event, gathers signals about them, and produces a score from 0 to 100. ## The check pipeline [#the-check-pipeline] ```mermaid graph LR; A[Your backend] -->|POST /v1/checks| B[Signal collection]; B --> C[Scoring]; C --> D{Rules}; D -->|allow| E[Grant benefit]; D -->|review| F[Ask for verification]; D -->|deny| G[Block]; ``` ## How the score is calculated [#how-the-score-is-calculated] Each signal $$s_i$$ has a weight $$w_i$$ and a confidence $$c_i \in [0, 1]$$. The raw score is their weighted sum, squashed into the 0 to 100 range: ```math \text{score} = 100 \cdot \left(1 - e^{-\sum_{i} w_i c_i}\right) ``` This means a single strong signal can push the score high, but many weak signals add up too. A score of 50 corresponds to a combined signal strength of $$\ln 2 \approx 0.69$$. ## Signals [#signals] Disposable domains, plus-address and dot aliases (`jane+1@gmail.com`, `j.ane@gmail.com`), and domains registered in the last 30 days. Known VPN, proxy and hosting provider ranges, and how many identities used the same IP in the last 24 hours. Device fingerprints collected by the browser SDK. A device that has already claimed a trial is one of the strongest signals we have. Previous claims linked to any part of the identity. See [benefit history](/docs/concepts/benefit-history). ## Decisions [#decisions] | Decision | Default score range | What you should do | | -------- | ------------------- | ------------------------------------------ | | `allow` | 0 to 39 | Grant the benefit. | | `review` | 40 to 74 | Ask for a card, phone number or SSO login. | | `deny` | 75 to 100 | Don't grant the benefit. | You can change these ranges, or ignore them entirely, with [rules](./rules.mdx). # Rules (/docs/concepts/rules) Rules run after scoring. Each rule has a condition and an action, and the first rule that matches decides the outcome. ## Writing conditions [#writing-conditions] Conditions are expressions evaluated against the check context. ```ts title="Block repeat trials" lineNumbers benefit.type == "free_trial" && benefit.previousClaims >= 1 && // [!code focus] score >= 40 ``` | Variable | Type | Example | | ------------------------ | --------- | ------------------ | | `score` | `number` | `62` | | `event` | `string` | `"trial_start"` | | `benefit.type` | `string` | `"credit"` | | `benefit.previousClaims` | `number` | `2` | | `signals` | `string[]`| `["vpn", "alias"]` | ## Evaluation order [#evaluation-order] Rules run from the lowest `priority` to the highest. If nothing matches, the default score ranges from [risk checks](/docs/concepts/risk-checks) apply. ```json title="rules.json" [ { "name": "Allow staff", "when": "identity.email.endsWith(\"@abusezero.com\")", "action": "allow", "priority": 1 }, { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1", "action": "deny", "priority": 10 }, // [!code ++] { "name": "Review VPN users", "when": "\"vpn\" in signals", "action": "review", "priority": 20 } // [!code --] ] ``` A broad `allow` rule with a low priority will hide every rule after it. Keep allow rules narrow. Deleted rules can't be restored. Disable a rule with `"enabled": false` if you might need it again. ## Testing a rule [#testing-a-rule] Try a condition against a sample check before you save it. Manage rules from the dashboard or with the [Rules API](/docs/api-reference/rules/listRules). # Errors (/docs/errors) Errors use standard HTTP status codes and return a JSON body with a machine-readable `code`. ```json { "code": "invalid_request", "message": "identity must include at least one of email, ip or deviceId", "docs": "https://abusezero.com/docs/errors#invalid_request" } ``` ## Error codes [#error-codes] | Status | Code | Meaning | | ------ | ----------------- | ---------------------------------------------------- | | 400 | `invalid_request` | The body is missing a field or has the wrong type. | | 401 | `unauthorized` | The API key is missing, revoked or for another mode. | | 404 | `not_found` | The check, benefit or rule doesn't exist. | | 409 | `conflict` | A rule with the same name already exists. | | 429 | `rate_limited` | Too many requests. Wait for `Retry-After` seconds. | | 500 | `internal_error` | Something went wrong on our side. Safe to retry. | ## Retrying safely [#retrying-safely] Send an `Idempotency-Key` header with every `POST`. If a request times out, retry with the same key and you'll get the original response instead of a duplicate check. The official SDKs add idempotency keys and retry `429` and `5xx` responses automatically. # FAQ (/docs/faq) Only if your rules tell it to. Start with `review` instead of `deny` for borderline scores, and ask for extra verification rather than refusing outright. The median check takes 40 ms and the 99th percentile is under 150 ms, measured from our edge locations. No, but device signals are the strongest predictor of repeat trials. Without them, checks rely on email, network and payment signals. Emails and payment fingerprints are hashed before storage. IP addresses are kept for 30 days. See our [privacy policy](/privacy) and [DPA](/dpa). Still stuck? [Contact us](/contact). # Introduction (/docs) AbuseZero checks every sign-up and benefit claim before you hand anything out. You send what you know about the person, and we return a risk score and a decision based on the rules you set. On this page ## How it works [#how-it-works] 1. A user starts a trial, redeems a coupon or claims credits. 2. Your backend calls [`POST /v1/checks`](/docs/api-reference/checks/createCheck) with their email, IP and device. 3. AbuseZero compares them against [benefit history](/docs/concepts/benefit-history) and returns `allow`, `review` or `deny`. 4. You grant the benefit, ask for more verification, or block it. AbuseZero is designed for products that give something away up front: free trials, sign-up credits, referral bonuses and promo codes. ## Where to start [#where-to-start] Run your first risk check in under five minutes. Learn how scores are calculated and which signals matter. Turn scores into decisions that match your business. Every endpoint, with a live playground. ## Use the docs with AI [#use-the-docs-with-ai] Every page is available as Markdown. Add `.md` to any docs URL, or use the **Copy Markdown** button at the top of the page. The full docs are also published at [`/llms.txt`](/llms.txt) and [`/llms-full.txt`](/llms-full.txt). # Quickstart (/docs/quickstart) This guide takes you from zero to a working risk check in your trial sign-up flow. ## Install the SDK [step] [#install-the-sdk-step] ```npm npm install @abusezero/node ``` ## Add your API key [step] [#add-your-api-key-step] Create a key in the dashboard and add it to your environment. Use a test key (`az_test_...`) while you build; it talks to the sandbox and never affects production data. ```bash title=".env" ABUSEZERO_API_KEY="az_test_4f9c2d7e8b1a" ``` ## Check the sign-up [step] [#check-the-sign-up-step] Call `checks.create` before you start the trial. ```ts tab="Node.js" title="app/api/trial/route.ts" import { AbuseZero } from '@abusezero/node'; const abusezero = new AbuseZero(process.env.ABUSEZERO_API_KEY); export async function POST(request: Request) { const { email, deviceId } = await request.json(); const check = await abusezero.checks.create({ event: 'trial_start', identity: { email, deviceId, ip: request.headers.get('x-forwarded-for') ?? undefined, }, benefit: { type: 'free_trial', plan: 'pro' }, }); if (check.decision === 'deny') { return Response.json({ error: 'Trial not available' }, { status: 403 }); } return Response.json({ trial: 'started' }); // [!code highlight] } ``` ```python tab="Python" title="app/trial.py" from abusezero import AbuseZero abusezero = AbuseZero(os.environ["ABUSEZERO_API_KEY"]) def start_trial(email: str, device_id: str, ip: str): check = abusezero.checks.create( event="trial_start", identity={"email": email, "device_id": device_id, "ip": ip}, benefit={"type": "free_trial", "plan": "pro"}, ) if check.decision == "deny": raise PermissionError("Trial not available") ``` ```bash tab="cURL" curl https://sandbox.abusezero.com/v1/checks \ -H "Authorization: Bearer $ABUSEZERO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42" }, "benefit": { "type": "free_trial", "plan": "pro" } }' ``` ## Record the benefit [step] [#record-the-benefit-step] Once the trial starts, tell AbuseZero. Future checks use this [benefit history](/docs/concepts/benefit-history) to spot repeat claims. ```ts await abusezero.benefits.record({ identity: { email, deviceId }, type: 'free_trial', checkId: check.id, }); ``` :::tip[You're done] Try starting a second trial with the same email in the sandbox. The second check comes back with a higher score and a `repeat_claim` signal. ::: ## Next steps [#next-steps] # Node.js (/docs/sdks/node) The Node.js SDK is fully typed. Hover over the code below to see the types. ## Installation [#installation] ```npm npm install @abusezero/node ``` ## Usage [#usage] ```ts twoslash // @filename: abusezero.d.ts declare module '@abusezero/node' { export type Decision = 'allow' | 'review' | 'deny'; export interface Check { id: string; /** Risk score from 0 to 100. Higher means more likely to be abuse. */ score: number; decision: Decision; signals: { name: string; weight: number }[]; } export class AbuseZero { constructor(apiKey?: string); checks: { create(input: { event: 'signup' | 'trial_start' | 'credit_claim' | 'coupon_redeem' | 'referral'; identity: { email?: string; ip?: string; deviceId?: string }; }): Promise; }; } } // @filename: index.ts // ---cut--- import { AbuseZero } from '@abusezero/node'; const abusezero = new AbuseZero(); const check = await abusezero.checks.create({ event: 'signup', identity: { email: 'jane@example.com' }, }); check.decision; // ^? ``` ## Configuration [#configuration] Pass options as the second argument to the constructor. ```ts const abusezero = new AbuseZero(process.env.ABUSEZERO_API_KEY, { timeout: 5000, failOpen: 'review', }); ``` ### ClientOptions | Prop | Type | Description | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | `apiKey?` | `string` | Secret API key. Defaults to the `ABUSEZERO_API_KEY` environment variable. | | `baseUrl?` | `string` | API base URL. Test keys use the sandbox automatically. Default: `'https://api.abusezero.com'` | | `failOpen?` | `union` | Decision to return when AbuseZero can't be reached, so an outage never blocks sign-ups. Default: `'allow'` | | `maxRetries?` | `number` | How many times to retry requests that fail with a network error or a 5xx status. Default: `2` | | `timeout?` | `number` | Request timeout in milliseconds. Default: `10000` | ## Failing open [#failing-open] If AbuseZero can't be reached within `timeout`, the SDK returns a synthetic check with the `failOpen` decision instead of throwing. Your sign-up flow keeps working during an outage. # Webhooks (/docs/sdks/webhooks) When you create a check with `"async": true`, AbuseZero responds right away and sends the result to your webhook endpoint when scoring finishes. ## Verifying signatures [#verifying-signatures] Every webhook includes an `AbuseZero-Signature` header. It's an HMAC-SHA256 of the timestamp and raw body, signed with your webhook secret: ```math \text{signature} = \operatorname{HMAC}_{\text{secret}}(\text{timestamp} \,\|\, \text{.} \,\|\, \text{body}) ``` ```ts title="app/api/webhooks/abusezero/route.ts" import { verifyWebhook } from '@abusezero/node'; export async function POST(request: Request) { const event = verifyWebhook( await request.text(), request.headers.get('abusezero-signature'), process.env.ABUSEZERO_WEBHOOK_SECRET ); if (event.type === 'check.completed') { // grant or block the benefit } return new Response(null, { status: 200 }); } ``` ```python title="webhooks.py" from abusezero import verify_webhook @app.post("/webhooks/abusezero") def handle(request): event = verify_webhook( request.body, request.headers["abusezero-signature"], os.environ["ABUSEZERO_WEBHOOK_SECRET"], ) if event.type == "check.completed": ... # grant or block the benefit return 200 ``` :::info Reject events whose timestamp is more than five minutes old to prevent replay attacks. `verifyWebhook` does this for you. ::: ## Retries [#retries] If your endpoint doesn't return a `2xx` status, we retry with exponential backoff for up to 24 hours. See the [`check.completed` event](/docs/api-reference/checks/checkCompleted) in the API reference for the payload. # Site map (/docs/site-map) Each dot is a page and each line is a link between two pages. Click a dot to open it. ## All guides [#all-guides] # Overview (/docs/api-reference) The AbuseZero API is a JSON REST API. Every endpoint page includes a playground, so you can send real requests to the sandbox with a test key. ## Base URLs [#base-urls] ```txt https://api.abusezero.com ``` ```txt https://sandbox.abusezero.com ``` ## Conventions [#conventions] - All endpoints are versioned under `/v1`. - Requests and responses use `application/json`. - Timestamps are ISO 8601 strings in UTC. - IDs are prefixed by type: `chk_` for checks, `ben_` for benefits and `rule_` for rules. ## Authentication [#authentication] Send your secret key as a bearer token. See [Authentication](/docs/authentication) for key types. ## Endpoints [#endpoints] # Create a risk check (/docs/api-reference/checks/createCheck) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Retrieve a risk check (/docs/api-reference/checks/getCheck) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Check completed (/docs/api-reference/checks/checkCompleted) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # List benefit claims (/docs/api-reference/benefits/listBenefits) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Record a benefit claim (/docs/api-reference/benefits/recordBenefit) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # List rules (/docs/api-reference/rules/listRules) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Create a rule (/docs/api-reference/rules/createRule) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Update a rule (/docs/api-reference/rules/updateRule) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } } # Delete a rule (/docs/api-reference/rules/deleteRule) { "openapi": "3.2.0", "info": { "title": "AbuseZero API", "version": "1.0.0-beta", "description": "Score sign-ups and benefit claims for abuse, keep a history of who claimed what,\nand manage the rules that decide the outcome.\n", "contact": { "name": "AbuseZero Support", "url": "https://abusezero.com/contact" } }, "servers": [ { "url": "https://api.abusezero.com", "description": "Production" }, { "url": "https://sandbox.abusezero.com", "description": "Sandbox" } ], "security": [ { "apiKey": [] } ], "tags": [ { "name": "Checks", "x-displayName": "Risk checks", "description": "Score a sign-up or benefit claim before you grant anything." }, { "name": "Benefits", "x-displayName": "Benefit history", "description": "Record and look up the benefits an identity has already claimed." }, { "name": "Rules", "description": "Configure the rules that turn a risk score into a decision." } ], "paths": { "/v1/checks": { "post": { "operationId": "createCheck", "tags": [ "Checks" ], "summary": "Create a risk check", "description": "Scores an identity against device, network, email and benefit history\nsignals, then applies your rules to return a decision.\n", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckRequest" }, "example": { "event": "trial_start", "identity": { "email": "jane@example.com", "ip": "203.0.113.42", "deviceId": "dev_9f8a7b6c" }, "benefit": { "type": "free_trial", "plan": "pro" } } } } }, "responses": { "201": { "description": "The check was scored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } }, "/v1/checks/{checkId}": { "get": { "operationId": "getCheck", "tags": [ "Checks" ], "summary": "Retrieve a risk check", "parameters": [ { "$ref": "#/components/parameters/CheckId" } ], "responses": { "200": { "description": "The requested check.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Check" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } } } }, "/v1/benefits": { "get": { "operationId": "listBenefits", "tags": [ "Benefits" ], "summary": "List benefit claims", "description": "Returns every benefit linked to an identity, newest first.", "parameters": [ { "name": "email", "in": "query", "description": "Email address to look up. Matching is normalized, so aliases count.", "schema": { "type": "string", "format": "email" } }, { "name": "deviceId", "in": "query", "description": "Device fingerprint from the AbuseZero browser SDK.", "schema": { "type": "string" } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } } ], "responses": { "200": { "description": "A page of benefit claims.", "content": { "application/json": { "schema": { "type": "object", "required": [ "data", "hasMore" ], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Benefit" } }, "hasMore": { "type": "boolean" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "recordBenefit", "tags": [ "Benefits" ], "summary": "Record a benefit claim", "description": "Call this after you grant a benefit so future checks can see it.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BenefitRequest" } } } }, "responses": { "201": { "description": "The benefit was recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Benefit" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules": { "get": { "operationId": "listRules", "tags": [ "Rules" ], "summary": "List rules", "responses": { "200": { "description": "All rules, in evaluation order.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Rule" } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } } }, "post": { "operationId": "createRule", "tags": [ "Rules" ], "summary": "Create a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" }, "example": { "name": "Block repeat trials", "when": "benefit.previousClaims >= 1 && benefit.type == \"free_trial\"", "action": "deny", "priority": 10 } } } }, "responses": { "201": { "description": "The rule was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } } } }, "/v1/rules/{ruleId}": { "parameters": [ { "name": "ruleId", "in": "path", "required": true, "schema": { "type": "string", "example": "rule_3kTq9" } } ], "patch": { "operationId": "updateRule", "tags": [ "Rules" ], "summary": "Update a rule", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RuleInput" } } } }, "responses": { "200": { "description": "The updated rule.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Rule" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }, "delete": { "operationId": "deleteRule", "tags": [ "Rules" ], "summary": "Delete a rule", "responses": { "204": { "description": "The rule was deleted." }, "404": { "$ref": "#/components/responses/NotFound" } } } } }, "webhooks": { "check.completed": { "post": { "operationId": "checkCompleted", "tags": [ "Checks" ], "summary": "Check completed", "description": "Sent when an asynchronous check finishes scoring.", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "type", "data" ], "properties": { "type": { "type": "string", "const": "check.completed" }, "data": { "$ref": "#/components/schemas/Check" } } } } } }, "responses": { "200": { "description": "Return any 2xx status to acknowledge the event." } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "Your secret API key, for example `az_live_...`. Use `az_test_...` keys against the sandbox." } }, "parameters": { "CheckId": { "name": "checkId", "in": "path", "required": true, "schema": { "type": "string", "example": "chk_7Hc2pQ" } } }, "responses": { "BadRequest": { "description": "The request was malformed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "Unauthorized": { "description": "The API key is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "NotFound": { "description": "The resource does not exist.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "RateLimited": { "description": "Too many requests. Retry after the number of seconds in `Retry-After`.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "schemas": { "Identity": { "type": "object", "description": "Everything you know about the person making the request. Send as much as you can.", "properties": { "email": { "type": "string", "format": "email" }, "ip": { "type": "string", "description": "IPv4 or IPv6 address of the client." }, "deviceId": { "type": "string", "description": "Device fingerprint from the AbuseZero browser SDK." }, "userId": { "type": "string", "description": "Your own ID for the user, if they already have an account." }, "paymentFingerprint": { "type": "string", "description": "Card fingerprint from your payment provider." } } }, "BenefitType": { "type": "string", "enum": [ "free_trial", "credit", "coupon", "referral" ] }, "CheckRequest": { "type": "object", "required": [ "event", "identity" ], "properties": { "event": { "type": "string", "enum": [ "signup", "trial_start", "credit_claim", "coupon_redeem", "referral" ] }, "identity": { "$ref": "#/components/schemas/Identity" }, "benefit": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/BenefitType" }, "plan": { "type": "string" }, "amount": { "type": "number", "description": "Value of the benefit in your account currency." } } }, "async": { "type": "boolean", "default": false, "description": "Score in the background and send a `check.completed` webhook." } } }, "Check": { "type": "object", "required": [ "id", "score", "decision", "signals", "createdAt" ], "properties": { "id": { "type": "string", "example": "chk_7Hc2pQ" }, "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Risk score. Higher means more likely to be abuse." }, "decision": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "matchedRule": { "type": [ "string", "null" ], "description": "ID of the rule that produced the decision." }, "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } }, "createdAt": { "type": "string", "format": "date-time" } } }, "Signal": { "type": "object", "required": [ "name", "weight" ], "properties": { "name": { "type": "string", "example": "disposable_email" }, "weight": { "type": "number", "description": "How much this signal contributed to the score." }, "detail": { "type": "string" } } }, "BenefitRequest": { "type": "object", "required": [ "identity", "type" ], "properties": { "identity": { "$ref": "#/components/schemas/Identity" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "checkId": { "type": "string", "description": "The check that approved this benefit." }, "amount": { "type": "number" } } }, "Benefit": { "type": "object", "required": [ "id", "type", "claimedAt" ], "properties": { "id": { "type": "string", "example": "ben_4Lm0x" }, "type": { "$ref": "#/components/schemas/BenefitType" }, "amount": { "type": "number" }, "checkId": { "type": "string" }, "claimedAt": { "type": "string", "format": "date-time" } } }, "RuleInput": { "type": "object", "required": [ "name", "when", "action" ], "properties": { "name": { "type": "string" }, "when": { "type": "string", "description": "Expression evaluated against the check context." }, "action": { "type": "string", "enum": [ "allow", "review", "deny" ] }, "priority": { "type": "integer", "default": 100, "description": "Lower numbers run first." }, "enabled": { "type": "boolean", "default": true } } }, "Rule": { "allOf": [ { "$ref": "#/components/schemas/RuleInput" }, { "type": "object", "required": [ "id", "createdAt" ], "properties": { "id": { "type": "string", "example": "rule_3kTq9" }, "createdAt": { "type": "string", "format": "date-time" } } } ] }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" }, "docs": { "type": "string", "format": "uri" } } } } } }