abusend: entegrasyon kılavuzu

abusend, zaten güvendiğiniz doğrulama araçlarının üzerinde çalışan bir kural katmanıdır: Cloudflare Turnstile, Google reCAPTCHA, hCaptcha, kendi SMS/OTP, e-posta, KYC akışlarınız veya başka herhangi bir akış. İş bağlamınız (hesap yaşı, tutar, ülke, kullanım, hız) üzerinde okunabilir kurallar yazarsınız; abusend her istek için kimin hangi doğrulamayı göreceğine karar verir, akışı (doğrulama adımı, yeniden deneme, kademeli doğrulama) SDK'ları üzerinden yürütür ve nedenini kaydeder. Her kural son trafiğe karşı yeniden oynatılabilir ve Uygulanıyor durumuna geçmeden önce İzleniyor durumunda denenir — izleme/uygulama arasındaki tek anahtar kuralın durumudur.

abusend ayrıca istemci sinyallerini ölçer (TLS/JA4, HTTP/2, tarayıcı probu, cihaz kimliği, IP verisi). Bunlar ucuz öncü sinyallerdir — ipucudur, hüküm değildir. Kararlı bir saldırgan her istemci tarafı kontrolü, her sinyali aşabilir; bu yüzden abusend varsayılan olarak hiçbir zaman tek bir sinyale dayanarak karar vermez ve ipuçlarını yalnızca sunucunuzun bildiği gerçeklerle birleştirmenizi sağlar.

English: INTEGRATION.md · Kural sözleşmesi: RULES.md · Gizlilik: PRIVACY.md

İçindekiler: Hızlı başlangıç · Tarifler (tek sayfalı uygulamalar ve React, Express, Next.js, sunucuda oluşturulan formlar, mobil ve API, uçtan uca SMS, abusend'in kendi doğrulamaları, öznitelikler ve bağlam, kod olarak yapılandırma, Nöbet, webhook'lar ve Slack, diğer diller) · Başvuru (kavramlar, kullanıcı kimlikleri, verdict isteği, verdict yanıtı, kararlar, onReview, 428 sözleşmesi, alanlar, eksik değerler, doğrulamalar, doğrulama grupları, akışlar, hatırlama, gönderim sınırı, izle → uygula, yeniden oynatma, test, cihaz kimliği, istemci IP'si, token'lar, hatalar ve kesintiler, reason kodları, hata kodları, hız limitleri, gizlilik, kontrol listesi, SSS)

tarayıcı (abusend.js)                abusend edge                          sizin sunucunuz
POST /v1/assess ───────────────────▶ sinyalleri topla + mühürle (karar yok)
◀──────────────── {token}
isteğiniz + X-Abusend-Token ───────────────────────────────────────────▶
                                     ◀── POST /v1/verdict {token, action, user, context}
                                     yerleşik kurallar → sizin kurallarınız, yukarıdan aşağı
                                     ──▶ allow | review | deny | challenge [{check: "sms"}]
◀── 428 {challenges: […]} (yalnızca challenge'da) ──────────────────────
SDK doğrulamaları çalıştırır (widget veya sizin OTP arayüzünüz), sonra yeni bir
token + X-Abusend-Challenge: ch_…[,ch_…] ile tekrar dener ─────────────▶ yeniden verdict: kurallar yeniden çalışır

Hızlı başlangıç

Üç adım, yaklaşık on dakika. Bir kuralı uygulamaya geçirene kadar kurallarınız hiçbir şeyi engellemez: yeni kurallar — her projenin başladığı iki önerilen kural dahil — İzleniyor durumunda kaydedilir. İşlemler (login, topup) yalnızca etikettir, trafikten oluşturulur; kendilerine ait bir modları yoktur.

1. Tarayıcı

<script src="https://rep.example.com/sdk/v1/abusend.js" data-site-key="pk_live_…"></script>
<script type="module">
  const res = await Abusend.fetch("/api/topup", { method: "POST", headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ amount: 100 }), action: "topup" });
</script>

Betik site anahtarını data-site-key özniteliğinden okur ve yüklendiği edge ile konuşur (data-endpoint bunu değiştirir). Abusend.fetch yeni bir token alır, X-Abusend-Token başlığıyla gönderir; sunucunuz 428 döndüğünde listelenen doğrulamaları çalıştırır, sonra yeni bir token ve X-Abusend-Challenge ile tekrar dener (en fazla 5 tur). abusend yüzünden asla reject etmez. Düz <form data-abusend-action="login"> de çalışır (sunucuda oluşturulan formlar); tek sayfalı uygulamalar yükleyici parçacığını, bir paketleyiciyi veya React'i kullanır (tarif).

2. Sunucu

import express from "express";
import { Abusend } from "@abusend/node";
import { userId } from "./user-id"; // kullanıcı kimliğinizin HMAC'i — bkz. "Kullanıcı kimliği seçimi"

const abusend = new Abusend({ secretKey: process.env.ABUSEND_SECRET_KEY!, endpoint: "https://rep.example.com" });

app.post("/api/topup", express.json(), abusend.protect({
  action: "topup",
  user: (req) => ({ id: userId(req.session.userId), attributes: { api_calls_total: req.session.apiCalls } }),
  context: (req) => ({ amount: req.body.amount, currency: "USD" }),
  onReview: "continue",                 // zorunlu: bu route'ta "Karar uygulamanızda" ne yapar
}), (req, res) => res.json({ ok: true }));

protect() (Express) ve guard() (Fetch API: Next.js, worker'lar) token ve challenge başlıklarını okur, POST /v1/verdict çağırır ve sizin yerinize yanıtlar: challenge'da doğrulama adımlarıyla 428, engellemede 403, "Karar uygulamanızda" durumunda sizin onReview seçiminiz. Kesintiler ve 429 review'a çevrilir, asla allow'a değil. onReview: "continue" ile, kurallarınızdan biri (veya token zorunluluğu) tarafından verilen bir review handler'ınıza devam eder; tamamlanmamış bir doğrulamadan ya da bir limitten gelen review ise 403 verification_incomplete ile yanıtlanır — yani bir doğrulamayı atlayan istemci, hızlı başlangıç ayarlarıyla asla geçemez (onReview).

3. İlk kuralınız, İzleniyor durumunda

Panel → Kurallar → Şablonlar → Uzun süre kullanılmamış hesap, büyük ilk yükleme → Kaydet.

user.api_calls_total == 0 && context.amount >= 100   →   SMS ile doğrula

Anahtarlar ve paketler

örnek nereye
edge URL'si https://rep.example.com script src, Abusend.configure({ endpoint }), Node new Abusend({ endpoint }) (veya ABUSEND_ENDPOINT)
site anahtarı (açık) pk_live_… / pk_test_… tarayıcı (data-site-key veya Abusend.configure({ siteKey }))
gizli anahtar sk_live_… / sk_test_… (panel → API anahtarları, bir kez gösterilir) yalnızca sunucunuz, ör. ABUSEND_SECRET_KEY

test ve live ayrı anahtarlara sahip ayrı projelerdir; geliştirirken test projesi kullanın (test). Live projelerde sitenizin Kurulum → İzinli origin'ler listesinde olması gerekir (https://shop.example.com veya https://*.example.com); listesi boş bir live proje her tarayıcı isteğini reddeder (origin_not_configured).

SDK paketleri herkese açık npm kayıt deposundan değil, edge'inizden sunulur:

npm install https://rep.example.com/sdk/v1/abusend-node.tgz      # sunucu: @abusend/node
npm install https://rep.example.com/sdk/v1/abusend-browser.tgz   # paketleyiciler / React: @abusend/browser

Import'lar @abusend/node / @abusend/browser olarak kalır. Tekrarlanabilir kurulum için sürümlü dosyayı sabitleyin (abusend-node-<sürüm>.tgz; güncel sürüm X-Abusend-SDK-Version yanıt başlığındadır). 503 sdk_tarball_unavailable, sunucunun tarball'lar olmadan derlendiği anlamına gelir (operatörler: sdk/build-tarballs.sh veya Docker imajı).

Paketlenmiş tarayıcı SDK'sı. Edge'in sunduğu betik, probun imza anahtarını içerir. Paketlenmiş bir kopya (npm, derleme adımı) anahtarı bir kez GET /v1/probe/pubkey adresinden ({"v":1,"pin":"…"}, açık bir anahtar) alır ve sabitler; bu isteği atlamak için kendiniz sabitleyin: configure({ siteKey, endpoint: "https://rep.example.com", probePin }) (React: <AbusendProvider probePin>). Anahtar alınamazsa prob kısıtlı çalışır ve istek probe.status = "failed" taşır — güçlü bir risk ipucu, asla bozuk bir sayfa değil.

Content Security Policy. Edge'e script-src ve connect-src içinde, bağladığınız widget doğrulamalarının sağlayıcılarına script-src ve frame-src içinde izin verin:

doğrulama script-src ve frame-src içine ekleyin
Cloudflare Turnstile https://challenges.cloudflare.com
Google reCAPTCHA (v3, v2, Enterprise) https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net
hCaptcha https://hcaptcha.com https://*.hcaptcha.com (ayrıca style-src ve connect-src)
Friendly Captcha script-src: https://cdn.jsdelivr.net/npm/@friendlycaptcha/ (SDK sabitlenmiş tek bir sürüm yükler); frame-src: https://*.frcapi.com (global ve AB API'si)
GeeTest v4 script-src: https://static.geetest.com https://gcaptcha4.geetest.com (GeeTest <script> / JSONP ile yanıt verir) ve yedek adresleri https://static.geevisit.com https://gcaptcha4.geevisit.com https://gcaptcha4.gsensebot.com; ayrıca https://static.geetest.com https://static.geevisit.com için style-src ve img-src, https://monitor.geetest.com için connect-src / img-src. Çerçeve (frame) yok. GeeTest CSP rehberi yayımlamıyor: politikanızı uygulamadan önce widget'ı onunla test edin.

| abusend'in kendi doğrulamaları (abusend_pow, abusend_hold, abusend_trace) | hiçbir şey: betik, çerçeve veya başka bir adres yok. İsteğe bağlı: worker-src blob: (politikanızda worker-src yoksa script-src blob:) SDK'nın bulmacayı Web Worker'larda çözmesini sağlar; bu izin olmadan SDK bulmacayı ana iş parçacığında küçük dilimler hâlinde çözer (daha yavaş, sayfa yanıt vermeye devam eder). |

SDK sağlayıcı betiklerini yalnızca bu adreslerden yükler; başka her şey script_blocked olarak raporlanır. Uygulama doğrulamaları (SMS, e-posta, KYC, custom) hiçbir şey yüklemez: arayüz sizindir.

Tarifler

Her tarif bir ekrana sığar ve tek bir yardımcı kullanır: kullanıcı kimliği seçimi bölümündeki userId().

Tek sayfalı uygulamalar ve React

Yükleyici parçacığı (paketleyici olmadan). Abusend.fetch() fonksiyonunu hemen çağıran kod, bir reklam engelleyici, bir CSP kuralı veya kararsız bir ağ abusend.js betiğini durdurduğunda bozulur (window.Abusend tanımsızdır). <script src> etiketinin yerine bu yükleyiciyi (tarayıcı SDK'sındaki loader.html) sayfanıza yapıştırın; son satırındaki edge URL'sini ve site anahtarını değiştirin (3000, ms cinsinden yükleme zaman aşımıdır):

<script>
/* abusend.js loader: loads the SDK and queues calls until it is ready. getToken()/execute()
   never reject: if the script is blocked (ad blocker, CSP, offline) or not loaded within
   `timeout` ms, they resolve "" and the verdict runs without a token. Abusend.fetch() then
   sends the request without a token; onChallenge() handlers are kept until the SDK loads. */
(function (w, d, src, siteKey, timeout) {
  if (w.Abusend) return;
  var sdk = null, state = "loading", confs = [], queue = [], done = [], hs = {};
  function run(c) {
    if (!sdk) return c[1](c[0] && c[0].fallback !== undefined ? c[0].fallback : "");
    sdk.execute(c[0]).then(c[1], function () { c[1](""); });
  }
  function settle(s) {
    if (state !== "loading") return;
    state = s;
    var q = queue; queue = [];
    for (var i = 0; i < q.length; i++) run(q[i]);
    for (i = 0; i < done.length; i++) done[i]();
  }
  function token(o) {
    return new Promise(function (resolve) {
      var c = [typeof o === "string" ? { action: o } : o, resolve];
      if (state === "loading" && !sdk) queue.push(c); else run(c);
    });
  }
  var D = w.Abusend = {
    version: "loader", getToken: token, execute: token,
    configure: function (o) { if (sdk) sdk.configure(o); else confs.push(o); return D; },
    protect: function (f, a) { f.setAttribute("data-abusend-action", a || "submit"); return f; },
    status: function () { return sdk ? sdk.status() : state; },
    challengeStatus: function (a) { return sdk ? sdk.challengeStatus(a) : { state: "none" }; },
    onChallenge: function (k, h) {
      if (sdk) return sdk.onChallenge(k, h);
      hs[k] = h;
      return function () { if (sdk) sdk.onChallenge(k, null); else if (hs[k] === h) delete hs[k]; };
    },
    fetch: function (u, i, o) { return D.ready.then(function () { return sdk ? sdk.fetch(u, i, o) : w.fetch(u, i); }); },
    ready: new Promise(function (r) { done.push(r); }),
    _loaded: function (api) {
      sdk = api; D.version = api.version; D.AbusendError = api.AbusendError;
      for (var i = 0; i < confs.length; i++) api.configure(confs[i]);
      for (var k in hs) if (hs[k]) api.onChallenge(k, hs[k]);
      settle("ready");
    }
  };
  var s = d.createElement("script");
  s.src = src; s.async = true; s.setAttribute("data-site-key", siteKey);
  s.onerror = function () { settle("unavailable"); };
  setTimeout(function () { settle("unavailable"); }, timeout);
  (d.head || d.documentElement).appendChild(s);
})(window, document, "https://rep.example.com/sdk/v1/abusend.js", "pk_live_…", 3000);
</script>

window.Abusend nesnesini hemen tanımlar ve SDK yüklenene kadar çağrıları sıraya alır. getToken() / execute() asla reject etmez; betik engellenir veya gecikirse Abusend.fetch() isteğinizi token'sız gönderir (o zaman doğrulama çalışamaz) ve erken kaydedilen onChallenge() işleyicileri SDK yüklendiğinde ona devredilir. Sıkı bir CSP altında satır içi betiğe sayfanızın nonce değerini verin.

Paketleyiciler (npm). Modül derlemesi kendi URL'sini bilmez: bir kez yapılandırın.

import { configure, fetch as abusendFetch, onChallenge } from "@abusend/browser";

configure({ siteKey: "pk_live_…", endpoint: "https://rep.example.com" });
onChallenge("sms", async (challenge, { signal }) => { /* OTP iletişim kutunuz */ });

const res = await abusendFetch("/api/topup", { method: "POST", body: JSON.stringify({ amount: 100 }), action: "topup" });

configure() olmadan hiçbir şey gönderilmez: execute() "" döndürür, getToken() not_configured ile reject eder ve SDK konsolda bir kez uyarır.

React. Uygulamayı AbusendProvider ile sarın (ya da useAbusend() hook'una siteKey ve endpoint verin veya açılışta bir kez configure() çağırın — bunların hiçbiri yoksa hook'un getToken() fonksiyonu "" döndürür ve bir kez uyarır):

import { AbusendProvider, useAbusend, useOnChallenge } from "@abusend/browser/react";

export function App() {
  return (
    <AbusendProvider siteKey="pk_live_…" endpoint="https://rep.example.com">
      <TopUpForm />
    </AbusendProvider>
  );
}

function TopUpForm() {
  const { fetch, ready, challenge } = useAbusend({ action: "topup" });
  useOnChallenge("sms", (ch, { signal }) => openOtpModal(ch, { signal })); // resolve = hemen tekrar dene; reject = vazgeç
  const verifying = challenge.state === "running" || challenge.state === "interactive";
  async function onSubmit(e: React.FormEvent) {
    e.preventDefault();
    const res = await fetch("/api/topup", { method: "POST", body: JSON.stringify({ amount: 100 }) });
    if (res.status === 428 || res.status === 403) showMessage("Bu isteği doğrulayamadık. Lütfen tekrar deneyin.");
  }
  return <form onSubmit={onSubmit}>…<button disabled={!ready || verifying}>{verifying ? "Doğrulanıyor…" : "Yükle"}</button></form>;
}

useAbusend({ action }) şunu döndürür: { fetch, getToken, ready, status, challenge }. fetch, hook'un işlemini varsayılan alan Abusend.fetch fonksiyonudur; challenge o işlemin son doğrulama adımıdır (challenge.state: none, running, interactive, completed, passed, failed, incomplete, …), böylece bir sayfadaki iki form birden "Doğrulanıyor…" göstermez. useOnChallenge(check, handler) bileşen takılıyken bir uygulama doğrulaması işleyicisi kaydeder. SDK tek bir örnektir (singleton): yapılandırma ve işleyiciler geneldir.

Express ile

app.set("trust proxy", 1);   // req.ip = yük dengeleyicinin arkasındaki istemci (client_ip olarak gönderilir)

app.post("/api/topup", express.json(), abusend.protect({
  action: "topup",
  user: (req) => ({ id: userId(req.user.id), attributes: { api_calls_total: req.user.apiCalls,
                                                           registered_at: req.user.createdAt.getTime() } }),
  context: (req) => ({ amount: req.body.amount, currency: req.body.currency }),
  checks: { sms: async (challenge, req) => {            // her yeni doğrulama adımı için bir kez çalışır (tekrar dönen adımda asla)
    req.session.otpChallenge = challenge.id;
    await sms.sendOtp(req.user.phone);
  } },
  onReview: "continue",
}), topupHandler);

Her seçenek isteğin bir fonksiyonu da olabilir (senkron veya async). Handler yalnızca allow için ve onReview ayarınızın devam ettirdiği review'lar için çalışır; verdict req.abusend üzerindedir.

Next.js route handler'ları

guard() Web Request / Response API'si üzerinde çalışır (Next.js App Router, Remix, Hono, worker'lar). Yalnızca başlıkları okur, istek gövdesini asla tüketmez.

// app/api/topup/route.ts
export async function POST(request: Request) {
  const session = await getSession(request);
  const body = await request.json();
  const blocked = await abusend.guard(request, {
    action: "topup",
    user: { id: userId(session.userId), attributes: { api_calls_total: session.apiCalls } },
    context: { amount: body.amount, currency: "USD" },
    checks: { sms: async (challenge) => { await saveOtpChallenge(session, challenge.id); await sms.sendOtp(session.phone); } },
    onReview: "continue",
  });
  if (blocked) return blocked;          // 428 doğrulama · 403 engel · sizin review yanıtınız
  return Response.json(await topup(session, body));
}

onReview, kendi kademeli doğrulamanız için (request, verdict) => Response | null fonksiyonu da olabilir; devam etmek için null döndürün (onReview). abusend.verdictOf(request) verdict'i sonradan döndürür. guard() olmadan abusend.verdict(…) çağırın ve challenge kararını Abusend.challengeResponse(verdict) (428 Response) ile yanıtlayın; diğer framework'ler için Abusend.challengeBody(verdict) { status, headers, body } verir.

Sunucuda oluşturulan formlar

SDK ile (önerilen). Formu işaretleyin; SDK formu Abusend.fetch(form.action, { method, body, action }) ile gönderir — varsayılan olarak URL kodlamalı, enctype="multipart/form-data" için FormData, GET için alanlar sorgu dizesinde —, sunucunun istediği doğrulamayı çalıştırır ve son yanıtı takip eder: bir yönlendirme izlenir (location.assign), başka bir yanıt belgenin yerine geçer. Form üzerindeki iptal edilebilir abusend:response olayı (event.detail.response) yanıtı kendiniz ele almanızı sağlar.

<form action="/login" method="post" data-abusend-action="login"> … </form>

Başarılı POST'ları bir yönlendirmeyle (303 See Other) yanıtlayın: belgenin yerine geçmek satır içi betikleri çalıştırmaz ve geçmişi güncellemez, bu yüzden yalnızca yedektir. Sunucunuz protect() / guard() fonksiyonunu yukarıdaki tariflerdeki gibi kullanır.

data-abusend-native bir formu bu davranışın dışında bırakır: gizli bir abusend_token alanıyla (data-abusend-field adını değiştirir) doğal yolla gönderilir ve doğrulama çalıştıramaz (orada bir doğrulama adımı tamamlanmamış olarak biter → doğrulamanın on_incomplete ayarı). X-Abusend-Token başlığı yoksa protect() bu gövde alanını kendisi okur (tokenField adını değiştirir); guard() ile ayrıştırdığınız formdan token: (req) => … verin.

JavaScript olmadan. Yalnızca uygulama doğrulamaları kullanın ve işlemi token'sız yapın (İşlemler → topup → Token: isteğe bağlı), çünkü token üretecek bir tarayıcı SDK'sı yoktur:

  1. POST /topup → abusend.verdict({ action: "topup", user, context, clientIp }).
  2. decision: "challenge" (challenges: [{check: "sms", …}]) → challenges içindeki kimlikleri oturuma kaydedin, OTP'yi gönderin, /verify sayfasına yönlendirin.
  3. /verify → kullanıcı kodu girer → sunucunuz kodu kontrol eder → abusend.challenges.result(id, "passed" | "failed", { userId }).
  4. Geri yönlendirin ve verdict'i challengeIds: ids ile tekrarlayın (başarıdan sonraki 5 dakika içinde) → kurallar yeniden çalışır; passed("sms") doğrudur → allow (veya sonraki adım).

Token'sız doğrulama adımları kullanıcıya bağlanır; burada her zaman user.id gönderin.

Mobil uygulamalar ve yalnızca API istemcileri

Yerel uygulamaların ve API istemcilerinin tarayıcı token'ı yoktur. İşlemlerini token'sız (isteğe bağlı) yapın: kurallar token.status = "missing", probe.status = "none" ve risk.level = "unknown" ile çalışır (IP verisi client_ip alanından gelir). Uygulama doğrulamalarını kullanın — widget doğrulamaları tarayıcı ister.

Her yüzey için ayrı bir işlem tutun (web için login, uygulama için app_login); böylece token zorunluluğu ve kurallar farklı olabilir.

Uçtan uca SMS doğrulaması

  1. Doğrulamayı bağlayın. Panel → Doğrulamalar → Ekle → SMS, anahtar sms. Uygulama doğrulamalarının varsayılanları: on_incomplete: deny, on_error: review, timeout_seconds: 600, max_issued_per_hour: 5, remember_seconds: 0. abusend SMS'i asla kendisi göndermez: doğrulamayı ister ve sunucunuzun bildirdiği sonucu kaydeder.

  2. Kural. Sonucu SMS ile doğrula olan herhangi bir kural, ör. hızlı başlangıçtaki şablon.

  3. Sunucu: kodu gönderin; protect() / guard() fonksiyonunun checks.sms kancasında. Kanca yalnızca bir doğrulama adımı ilk kez verildiğinde çalışır (challenge.reissued false), yani tekrarlar asla yeniden SMS göndermez. "Kodu tekrar gönder" düğmesi sizin kendi uç noktanızdır.

  4. Tarayıcı: arayüzünüzü gösterin.

    Abusend.onChallenge("sms", async (challenge, { signal }) => {
      await openOtpModal({ challengeId: challenge.id, signal }); // resolve = hemen tekrar dene; reject / abort = vazgeç
    });
    
  5. Sunucu: doğrulayın ve bildirin. failed sonucunu yalnızca kendi deneme sınırınızdan sonra bildirin — yanlış yazılmış bir kod başarısız bir doğrulama değildir:

    app.post("/otp/verify", express.json(), async (req, res) => {
      const id = req.session.otpChallenge;                        // veya req.body.challengeId (userId bağı, kullanıcılar arası kullanımı durdurur)
      const ok = await sms.checkOtp(req.user.phone, req.body.code);
      const tries = (req.session.otpTries ??= {}); tries[id] = (tries[id] ?? 0) + 1;
      if (!ok && tries[id] < 3) return res.status(400).json({ retry: true });   // kullanıcı tekrar yazsın
      try {
        await abusend.challenges.result(id, ok ? "passed" : "failed", { userId: userId(req.user.id) });
      } catch (e) {
        if (e.code === "challenge_resolved") return res.status(409).json({ restart: true }); // zaten bırakılmış veya süresi dolmuş
        throw e;
      }
      res.json({ ok });
    });
    
  6. Tekrar. Modal resolve olur, Abusend.fetch yeni bir token ve X-Abusend-Challenge ile tekrar dener ve verdict kuralları yeniden çalıştırır: SMS başarılı → sonraki kural veya allow; başarısız → doğrulamanın on_fail ayarı (Engelle).

Kullanıcı modalı kapatırsa handler reject eder ve SDK doğrulama adımını abandoned (bırakıldı) olarak bildirir (ilk bildirim kazanır: sonradan gelen passed 409 challenge_resolved alır). O kullanıcının bu işlem için sonraki verdict'i tamamlanmamış doğrulamayı devralır ve doğrulamanın on_incomplete ayarıyla yanıtlanır (uygulama doğrulamalarında Engelle); ondan sonraki deneme, gönderim sınırı içinde yeni bir akış başlatır. Kimsenin bildirmediği bir doğrulama adımı timeout_seconds dolunca incomplete olur.

abusend'in kendi doğrulamaları

abusend'in kendisinin doğruladığı üç widget doğrulaması: sağlayıcı hesabı, site anahtarı, gizli anahtar veya üçüncü taraf betiği yok; abusend edge'iniz dışında kimseye bir şey gönderilmez.

sağlayıcı ziyaretçinin yaptığı ne zaman kullanılır
abusend_pow görünür bir şey yok: SDK arka planda bir iş kanıtı (proof-of-work) bulmacası çözer her denemeye işlemci süresi maliyeti ekleyen, risk arttıkça büyüyen bir ilk adım
abusend_hold doğrulama penceresinde iki noktaya sırayla basılı tutar hızlı, görünür bir adım (fare, dokunma veya kalem)
abusend_trace işaretçiyi veya parmağını 3–5 sn boyunca bir kutu içinde gezdirir puanlanacak daha çok hareket içeren görünür bir adım
  1. Bağlayın. Panel → Doğrulamalar → Ekle → üçünden biri. Yapıştırılacak bir şey yok: doğrulama hemen hazırdır (Test tamam yanıtı verir; ulaşılacak bir sağlayıcı yoktur). Bulmaca boyutunu seçin: verdict'in risk seviyesine göre (varsayılan low / medium / high / critical için 15 / 18 / 20 / 22; her adım işi ikiye katlar; tarayıcı token'ı yoksa low sayılır), sabit ya da kapalı (yalnızca hold ve trace). Form çözüm süresini kendi tarayıcınızda ölçerek tahmin eder (telefon yaklaşık 4 kat yavaş) ve bulmacayı denemenizi sağlar. Hold ve trace için katılığı (lenient · normal · strict) seçin ve görevi formun önizlemesinde deneyin: puanı ve puanı neyin düşürdüğünü gösterir.
  2. Kural. Her widget gibi ‹doğrulama› ile doğrula. Tarayıcı SDK'sı 3.3.0 veya sonrası gerekir (eski SDK'lar modu widget_error olarak bildirir: doğrulamanın "doğrulanamadı" sonucu, asla İzin ver değil).
  3. Denemeler. İkna etmeyen bir hold veya trace kısa bir ipucuyla baştan başlar (doğrulama adımı başına en fazla 3 deneme). Son denemeden sonra başarısız olur (not_human_like → on_fail) ya da görev yapılmadıysa tamamlanmadı sayılır (task_incomplete → on_incomplete). Yanlış bulmaca yanıtı hemen başarısız olur (pow_invalid); aynı kaydın yeniden gönderilmesi token_reused olarak başarısız olur.
  4. Yalnızca klavye kullanan ziyaretçiler hold veya trace yapamaz. Onların geçmesi gerekiyorsa görünür bir görevi tek yol yapmayın: yapabilecekleri bir doğrulamayla eşleştirin (örneğin SMS ile bir Bunlardan biri grubu — seçim ağırlığa göre rastgeledir, yani ziyaretçiye ikisinden biri düşebilir), yapılamayan bir görev uygulamanıza ulaşsın diye on_incomplete: review bırakın ya da etkileşim gerektirmeyen abusend_pow kullanın.

Her istemci tarafı doğrulama gibi bunlar da aşılabilir: çözülmüş bir bulmaca bir kişiyi değil, harcanmış işlemci süresini kanıtlar; hareket kaydedilebilir veya üretilebilir. Hareket puanı sezgisel kurallardan gelir — katılığın geçti / başarısız'a çevirdiği bir ipucu. Görev kutusundaki işaretçi olayları yalnızca edge'inize gider; orada puanlanır ve saklanmaz (doğrulama adımı durumunu, ayrıntı kodunu, puanı ve bir günlük tekrar özetini tutar).

Veri biçimi (kendi istemciniz için): doğrulama adımı mode (sağlayıcı adı) ve params taşır — pow: {alg: "sha256", seed, bits, puzzles, work} (her i < puzzles için SHA-256("<seed>.<i>.<n>") değeri bits sıfır bitle başlayan bir n bulun; doğrulamanın bulmacası yoksa yer almaz) ve task: {type: "hold", targets: [{x, y}, {x, y}], hold_ms} (kutunun oranları olarak) veya {type: "trace", duration_ms}. Yanıt: POST /v1/challenges/{id}/verify {"site_key": "pk_…", "solution": {"pow": [n, …], "telemetry": {"v": 1, "w", "h", "dpr", "pt": "mouse" | "touch" | "pen", "ev": [[t, x, y, type], …], "ut": 0}}} (ev: en fazla 3000 olay; t kutu gösterildiğinden beri ms, x / y kutu içinde CSS pikseli, type 0 hareket · 1 basma · 2 bırakma; ut: sayfanızın kendisinin gönderdiği olaylar). Deneme hakkı kaldıkça geçmeyen bir deneme {"status": "pending", "retry": true, "missing": "hold_short"} yanıtını alır — missing presses, target_missed, hold_short, trace_short veya trace_small olur; görev yapıldı ama ikna etmediyse yer almaz; görevi yeniden çalıştırın.

Öznitelikleri tanımlamak ve bağlam göndermek

Olaylar

Bazı gerçekleri abusend göremez: tamamlanan bir yükleme, bir ödeme, bir para çekme. Sunucunuz bunları gerçekleştikten sonra bildirir; kurallar da onları kullanıcı başına bir zaman aralığında sayar.

// ödeme başarılı olduktan sonra
const r = await abusend.events.track({
  userId: hmac(session.userId),    // verdict'lerinizdeki aynı opak kimlik
  type: "topup",                   // ^[a-z0-9_]{1,32}$
  value: payment.amount,           // event_sum bunu toplar
  id: payment.id,                  // idempotency: yeniden gönderilen olay bir kez saklanır
});
// asla hata fırlatmaz: r.ok === false ise r.error vardır (onError'a da gider)
curl -sS https://rep.example.com/v1/events \
  -H "Authorization: Bearer $ABUSEND_SECRET_KEY" -H "Content-Type: application/json" \
  -d '{"events": [{"user_id": "u_5f1c9a…", "type": "topup", "value": 250, "id": "pay_81f3",
                   "occurred_at": "2026-09-28T09:30:00Z"}]}'
# → {"accepted": 1, "duplicates": 0}

Bunlar üzerine kurallar (yalnızca "1h", "24h", "7d", "30d" aralıkları):

event_count("topup", "1h") >= 5            → SMS ile doğrula          (bir saatte zaten 5 yükleme)
event_sum("topup", "24h") > 2000           → Karar uygulamanızda      (24 saatte 2000'den fazla yükleme)
days_since(event_last("withdraw")) < 1     → SMS ile doğrula          (son bir günde para çekme)

İlk ikisi şablon olarak da var (Kısa sürede çok sayıda yükleme, 24 saatte yüksek toplam) ve her yeni kural gibi İzleniyor durumunda kaydedilir.

Ortak sayaçlar

Yukarıdakilerin hepsi tek bir kullanıcıya aittir. Bir sayaç tüm kullanıcıları toplar — göndermeniz gereken bir şey yoktur: bir işlemdeki denemeleri (her verdict çağrısı; doğrulamadan sonraki yeniden deneme tekrar sayılmaz) ya da sunucunuzun zaten bildirdiği olayları son bir zaman aralığında sayar; isterseniz gruba göre. Panelde (Koruma → Sayaçlar) veya kod olarak yapılandırmada tanımlayın, sonra bir kuralda counter("key", "1h") ile okuyun (aralık en fazla "24h"): isteğin grubunun toplamı.

Ülkeye göre ödemeler: "payment" denemelerini ip.country bazında sayar
  ip.country == "SG" && counter("payments_by_country", "1h") > 100    → SMS ile doğrula

Hotmail satın almaları: "purchase" olaylarının değerini toplar, yalnızca user.email "hotmail" içeriyorsa
  user.email contains "hotmail" && counter("hotmail_purchases", "15m") > 50000   → Captcha ile doğrula

Kod olarak yapılandırma

Projenin yapılandırmasını git'te tutun, değişiklikleri bir pull request'te gözden geçirin ve test projenizde denediğiniz kurulumu canlı projeye taşıyın: panel → Proje ayarları → Kod olarak yapılandırma [admin].

apiVersion: abusend/v1
kind: ProjectConfig
checks:
  - {key: captcha, provider: turnstile, config: {site_key: 0x4AAA…}}
  - {key: sms, provider: sms, max_issued_per_hour: 5}
rules:                       # bu sırayla değerlendirilir
  - key: big_new_account
    name: Yeni hesaptan büyük alım
    when: hours_since(user.registered_at) < 1 && context.amount_usd > 50
    then: challenge
    checks: {mode: sequence, items: [{check: captcha}, {check: sms}]}
    actions: [pay]
    state: monitoring        # monitoring | enforcing | off

Arkasındaki API (bir kişisel erişim tokenı, abp_…, da çalışır): GET /api/panel/v1/projects/{p}/config (YAML), POST …/config/plan {yaml, prune} ve POST …/config/apply {yaml, prune, base, confirm_enforce, secrets}.

Nöbet (Watch)

Nöbet trafiğinizi günün her saati izler; alışılmadık bir şey olduğunda dakikalar içinde haber verir, ardından kendi yapay zekâ asistanınızın incelemesini ve raporlamasını sağlar. Varsayılan olarak kapalıdır: panel → İzleme → Nöbet [admin]. Kodunuzda hiçbir şey gerektirmez.

Webhook'lar ve Slack

abusend raporları ve uyarıları bir webhook'a ya da bir Slack kanalına iletebilir: panel → Proje ayarları → Bildirimler [admin], projede en fazla beş hedef. Her hedefin bir https:// adresi (şifreli saklanır, gizlenmiş gösterilir: bir Slack gelen webhook adresi bir sırdır), bir biçimi, aldığı olaylar ve oluştururken bir kez gösterilen bir imza sırrı (whsec_…) vardır. Test gönder, uç noktanızı ve doğrulama kodunuzu denemeniz için bir test olayı sıraya alır.

Olaylar: watch.report (bir rapor), watch.alert (bir anomali uyarısı), test.

JSON biçimi — olay başına bir POST, Content-Type: application/json:

{
  "event": "watch.alert",
  "id": "0198f1c2-7d3e-7a10-9c55-2f4c1b8e6a01",
  "created_at": "2026-09-28T10:00:00Z",
  "project": {"id": "0198a0c4-…", "name": "Acme cüzdan"},
  "data": {"project": "Acme cüzdan", "title": "login: denemeler olağan seviyenin 10 katı", "detail": "son 15 dakika içinde \"login\" işlemine 6180 deneme geldi (dakikada yaklaşık 412); bu saatte olağan seviye dakikada yaklaşık 38 (önceki 7 günün aynı saatlerinin ortancası).", "current": "412 / dk", "baseline": "38 / dk", "url": "https://panel.example.com/p/0198a0c4-…/watch"}
}
başlık değer
Abusend-Event olayın adı
Abusend-Delivery gönderim kimliği (gövdedeki id); her yeniden denemede aynıdır: yinelenenleri yok saymak için kullanın
Abusend-Timestamp Unix saniyesi, her denemede yeniden belirlenir
Abusend-Signature v1= + <timestamp>.<ham gövde> üzerinde sırla (whsec_… dizesinin tamamı) hesaplanan HMAC-SHA256'nın onaltılık gösterimi

Olayların data alanı (sayılar, işlem, bir ülke kodu ya da ASN ve modelin sözleri; bir sayaçla ilgili not ayrıca sayacın gruplandığı değerlerden en fazla beşini taşır, bunlar IP adresi, son kullanıcı kimliği ya da cihaz kimliği olabilir; asla e-posta adresi değil; metin, Nöbet ayarlarını en son kaydeden kişinin dilindedir):

olay data
watch.alert project (ad), title (tek satır: ne arttı; durum güncellemesi "Devam ediyor:", büyüyen bir bulgu "Büyüyor:", bitiş "Normale döndü:" ile başlar), detail (sayılarla bir iki cümle), current ve baseline (birimiyle, örn. "412 / dk", "%45"), url (Nöbet sayfası; panel adresi ayarlı değilse boş)
watch.report project, window ("2026-09-28 14:02 UTC": incelemenin çalıştığı an), summary (asistanın Markdown raporu, en fazla 1200 karakter: başlıklar, listeler, **kalın**, `kod`, bağlantılar), summary_format (her zaman "markdown": işleyin ya da metin olarak gösterin), findings ([{title, detail}]: tarayıcının bulduğu), proposals (sahibini bekleyen önerilen değişiklik sayısı), url (?run=<id> ile Nöbet sayfası)
test message

Bir uyarının data alanında ayrıca aşağıdaki isteğe bağlı anahtarlar bulunabilir; boş olanlar yer almaz, baseline de boş olabilir.

anahtar anlamı
group bulgunun ait olduğu değer (bir IP adresi, kullanıcı kimliği, ülke, …); bir not ya da yaklaşma bulgusu tam olarak tek bir değerle ilgiliyse dolu olur
group_label group'un ne olduğu, örn. "IP adresi"
baseline_label baseline olağan seviye değilse ne anlama geldiği, örn. "Eşik"
current_label current'ın ne anlama geldiği, örn. "Şu an en yüksek"
kind alarm olmayan bir mesaj için "note" (bir kural devreye girdi ya da devreden çıktı), anomali için "alert"; bir notun title alanında "Uyarı:" öneki olmaz
lang metinlerin dili, "en" ya da "tr"
rule notun ilgili olduğu kural, örn. "Top-up IP burst" kuralı
rule_action kuralın ne yaptığı, örn. "captcha ile doğrula · Uygulanıyor"
counter sayacın tek satırlık özeti, örn. "topup_ip_burst · IP adresi başına · son 15 dakika"
count bir sayı: notun kaç değerle ilgili olduğu (bir dönemin bitişinde, aynı anda en çok kaç değer için devrede olduğu); bir not bir kuralın tüm değerlerini kapsar
count_label count'ın neyi saydığı, örn. "IP adresi sayısı"
examples en fazla beş {group, value} (en yüksek değerler ya da son nottan bu yana yeni olanlar; value metindir)
examples_label examples başlığı, örn. "En yüksek değerler"
next ne yapılacağı ve sonraki notun ne zaman geleceği

Bir sayacın değerleri için devreye giren bir kuralla ilgili not, örneğin:

"data": {"project": "Acme cüzdan", "kind": "note", "lang": "tr", "title": "\"Top-up IP burst\" kuralı 143 IP adresi için devreye girdi", "detail": "Uygulanıyor durumundaki \"Top-up IP burst\" kuralı artık 143 IP adresi için devrede. …", "current": "1.377", "current_label": "Şu an en yüksek", "baseline": "≥ 20", "baseline_label": "Eşik", "rule": "\"Top-up IP burst\" kuralı", "rule_action": "captcha ile doğrula · Uygulanıyor", "counter": "topup_ip_burst · IP adresi başına · son 15 dakika", "count": 143, "count_label": "IP adresi sayısı", "examples": [{"group": "198.51.100.81", "value": "1.377"}], "examples_label": "En yüksek değerler", "next": "Bu bir alarm değil, bilgi notudur: …", "url": "https://panel.example.com/p/0198a0c4-…/watch"}

Uyarılar ve raporlar Nöbet ayarlarındaki Uyarıları gönder / Raporları gönder anahtarlarına uyar (e-postaya da webhook'lara da aynı şekilde uygulanır).

Her gönderimi doğrulayın: HMAC'i ham istek gövdesi üzerinde yeniden hesaplayın (yeniden serileştirilmiş bir kopya üzerinde değil), sabit zamanlı karşılaştırın ve birkaç dakikadan eski zaman damgalarını reddedin (tekrar oynatma). Node.js (Express):

import crypto from "node:crypto";
import express from "express";

const secret = process.env.ABUSEND_WEBHOOK_SECRET; // whsec_…
const app = express();

// The signature covers the raw bytes: read the body raw, parse it afterwards.
app.post("/abusend-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.get("Abusend-Timestamp") ?? "";
  const sig = req.get("Abusend-Signature") ?? "";
  const want = "v1=" + crypto.createHmac("sha256", secret).update(`${ts}.`).update(req.body).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  const ok = fresh && sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
  if (!ok) return res.status(400).end();

  const delivery = JSON.parse(req.body.toString("utf8"));
  // delivery.event: "watch.report" | "watch.alert" | "test"; delivery.id dedupes retries.
  res.status(204).end();
});

Go:

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"math"
	"net/http"
	"strconv"
	"time"
)

// verify checks Abusend-Signature over "<timestamp>.<raw body>".
func verify(secret string, r *http.Request, body []byte) bool {
	ts := r.Header.Get("Abusend-Timestamp")
	n, err := strconv.ParseInt(ts, 10, 64)
	if err != nil || math.Abs(float64(time.Now().Unix()-n)) > 300 {
		return false
	}
	m := hmac.New(sha256.New, []byte(secret))
	m.Write([]byte(ts + "."))
	m.Write(body)
	want := "v1=" + hex.EncodeToString(m.Sum(nil))
	return hmac.Equal([]byte(want), []byte(r.Header.Get("Abusend-Signature")))
}

func handler(secret string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
		if err != nil || !verify(secret, r, body) {
			http.Error(w, "bad signature", http.StatusBadRequest)
			return
		}
		// decode body; answer 2xx quickly
		w.WriteHeader(http.StatusNoContent)
	}
}

Slack hedefleri bunun yerine bir gelen webhook gövdesi alır (text ve blocks: başlık, özet, proje ve olay, panele giden bir düğme); Markdown özet Slack biçimine dönüştürülür (*kalın*, <url|metin>, • madde imleri), uzunsa birkaç bölüme ayrılır, sığmazsa rapora giden bir bağlantıyla biter. Aynı başlıklarla imzalanır, Slack bunları yok sayar.

Gönderim. 10 saniye içinde 2xx ile yanıt verin. 5xx, 408, 429, zaman aşımları ve bağlantı hataları 1 dakika, 5 dakika, 30 dakika, 2 saat ve 6 saat sonra yeniden denenir (toplam altı deneme, sonra gönderimden vazgeçilir); yönlendirmeler izlenmez ve diğer her 4xx hemen başarısız sayılır, adresi güncelleyin. İstekler abusend sunucularından gelir; üretim kurulumunda özel ve iç adresler reddedilir.

Diğer diller

Her dil çalışır: bir HTTPS çağrısı, beş sonuç.

curl -sS https://rep.example.com/v1/verdict \
  -H "Authorization: Bearer $ABUSEND_SECRET_KEY" -H "Content-Type: application/json" \
  -d '{"token":"abt_…","action":"topup","client_ip":"203.0.113.7",
       "user":{"id":"3f9a1c…","attributes":{"api_calls_total":0}},
       "context":{"amount":100,"currency":"USD"},
       "challenge_ids":["ch_…"],"review_handling":"custom"}'
import hashlib, hmac, os, requests

session = requests.Session()
session.headers["Authorization"] = "Bearer " + os.environ["ABUSEND_SECRET_KEY"]

def user_id(internal_id):  # opak kimlik: yalnızca sizin bildiğiniz anahtarla HMAC, asla e-posta değil
    return hmac.new(os.environ["ABUSEND_USER_ID_KEY"].encode(), str(internal_id).encode(), hashlib.sha256).hexdigest()

def verdict(request, action, user, context):
    body = {"token": request.headers.get("X-Abusend-Token", ""), "action": action, "client_ip": request.remote_addr,
            "user": user, "context": context, "review_handling": "custom"}
    if request.headers.get("X-Abusend-Challenge"):                # "ch_…" veya "ch_…,ch_…" (en fazla 3)
        body["challenge_ids"] = [i.strip() for i in request.headers["X-Abusend-Challenge"].split(",") if i.strip()][:3]
    try:
        r = session.post(os.environ["ABUSEND_URL"] + "/v1/verdict", json=body, timeout=1.5)
    except requests.RequestException:
        return {"decision": "review", "degraded": True}          # kesinti: asla allow değil
    if r.status_code == 429 or r.status_code >= 500:
        return {"decision": "review", "degraded": True}          # 429'a bir saldırgan yol açabilir
    r.raise_for_status()                                          # diğer 4xx: entegrasyon hatası
    return r.json()

# v = verdict(request, "topup", {"id": user_id(current_user.id)}, {"amount": 100})
# challenge → 428 {"error": "challenge_required", "challenges": v["challenges"]}, başlık Abusend-Challenge: 1
# deny → 403 · review → sizin kararınız (decided_by'a bakın) · allow → devam

Panelin her işlemde "Karar uygulamanızda" durumunun ne yaptığını gösterebilmesi için review_handling (continue, block veya custom) gönderin. Bilgi amaçlıdır: kararı asla değiştirmez.

Başvuru

Kavramlar

kavram anlamı
İşlem (action) Korunan bir akış (login, topup): token required · optional ve token_missing review · deny taşıyan bir etiket. Trafikten (veya elle) oluşturulur. İşlemlerin modu yoktur: uygulanan kurallarınız bir işleme ilk isteğinden itibaren uygulanır.
Sinyal (signal) abusend'in ölçtüğü şey: TLS/JA4, HTTP/2, prob, tarayıcı özellikleri, IP (veri merkezi, VPN, Tor, relay, ülke, ASN), cihaz, hız. Sinyaller kural alanıdır ve ipucudur, hüküm değildir.
Risk Türetilmiş bir sinyal: yerleşik ağırlıklı sinyal tanımlarından risk.score 0–100 ve risk.level low · medium · high · critical · unknown (ağırlıklar proje başına Sinyaller altında değiştirilebilir; seviyelerin başladığı puanlar proje başına eşiklerdir, varsayılan 25 / 50 / 75). Tek başına asla karar vermez.
Bağlam (context) Sunucunuzun gönderdiği: user.attributes (kalıcı) ve context (bu istek). Tiplidir.
Kural (rule) when <koşul> → allow · review · deny · challenge(doğrulama grubu); sıralı; isteğe bağlı işlem kapsamı; durum Kapalı · İzleniyor · Uygulanıyor — izleme/uygulama arasındaki tek anahtar.
Doğrulama (check) Bağlı bir doğrulama aracı. Widget: turnstile, recaptcha, recaptcha_enterprise, hcaptcha, friendly_captcha, geetest ve abusend'in kendi abusend_pow, abusend_hold, abusend_trace doğrulamaları — tarayıcı SDK'sı çalıştırır, edge doğrular. Uygulama: sms, email, kyc, custom — uygulamanız çalıştırır, sunucunuz bildirir.
Doğrulama adımı (challenge) Bir doğrulamanın verilmiş tek bir örneği: pending → passed · failed · incomplete · error (süresi dolmuş bekleyen adım incomplete sayılır).
Doğrulama grubu (check group) Bir doğrulama kuralının istediği: 1–3 doğrulama; bunlardan biri (rastgele, ağırlığa göre), hepsi, sırayla veya hepsi aynı anda (doğrulama grupları). Tek doğrulama, tek elemanlı bir gruptur.
Akış (flow) Tek bir denemenin verdict → doğrulama adım(lar)ı → son verdict zinciri. En fazla projenin doğrulama bütçesi kadar adım (Proje ayarları → Deneme başına doğrulama: 1–5, varsayılan 3; verilen her adım sayılır, birlikte verilenler dahil), her doğrulama bir kez.

Panel sözcükleri: allow İzin ver · review Karar uygulamanızda · deny Engelle · challenge ‹doğrulama› ile doğrula (gruplar: "Captcha veya hCaptcha ile doğrula (70/30)", "önce Captcha, sonra SMS ile doğrula", "aynı anda Captcha ve SMS ile doğrula") · bir challenge Doğrulama adımı · kural durumları İzleniyor · Uygulanıyor · Kapalı (yalnızca kurallar; işlemlerin durumu yoktur) · sinyaller (ipucu) etiketi taşır. Tam sözleşme: RULES.md.

Kullanıcı kimliği seçimi

user.id, verdict'leri, akışları, listeleri ve silmeyi kullanıcılarınızdan birine bağlar. Opak ve kalıcı olmalıdır; asla e-posta adresi, telefon numarası, T.C. kimlik numarası veya ad olmamalıdır.

import { createHmac } from "node:crypto";
export const userId = (internalId: string) =>
  createHmac("sha256", process.env.ABUSEND_USER_ID_KEY!).update(String(internalId)).digest("hex");

Verdict isteği

POST /v1/verdict, Authorization: Bearer sk_…, JSON gövde (en fazla 64 KiB):

alan
token string, isteğe bağlı Tarayıcının token'ı (X-Abusend-Token). Yoksa → token_status: "missing" (bkz. kararlar).
action string ^[a-z0-9_.-]{1,32}$. Zorunlu: işlemin token zorunluluğunu ve kapsamdaki kuralları seçer; token'ın işlemiyle karşılaştırılır (checks.action).
user nesne, isteğe bağlı {"id": "…", "attributes": {…}}; id 1–256 karakter, opak.
context nesne, isteğe bağlı Bu isteğin gerçekleri (amount, currency, …). ≤ 32 anahtar, ≤ 4 KB.
client_ip string, isteğe bağlı Sunucunuzun gördüğü son kullanıcı IP'si (port olmadan). Bkz. istemci IP'si.
challenge_ids dizi, isteğe bağlı Tekrarlanan isteğin X-Abusend-Challenge başlığındaki (orada virgülle ayrılmış) 0–3 ch_… kimliği. 3'ten fazlası veya hatalı bir kimlik → 400 invalid_request; kullanılamayan bir kimlik asla HTTP hatası değildir, yalnızca kredi vermez.
review_handling string, isteğe bağlı continue · block · custom: sunucunuzun review ile ne yaptığı. Bilgi amaçlı (panel).
simulate string, isteğe bağlı allow · review · deny. Yalnızca test projeleri; live projeler yok sayar.

Verdict yanıtı

İyi biçimlendirilmiş her istek için HTTP 200 — kullanılamayan token'lar dahil.

{ "id": "0192…", "flow_id": "fl_…", "decision": "challenge", "would_decision": null, "would_challenge": null,
  "decided_by": {"type": "rule", "id": "…", "name": "Dormant account, big first top-up", "code": "rule_3c1f09aa"},
  "challenges": [{"id": "ch_…", "check": "sms", "name": "SMS", "kind": "app", "provider": "sms", "expires_in": 600, "reissued": false}],
  "risk": {"score": 12, "level": "low"},
  "reasons": [
    {"code": "rule_3c1f09aa", "label": "Dormant account, big first top-up", "kind": "rule", "effect": "challenge", "rule_id": "…", "category": "account"},
    {"code": "ip_vpn", "label": "VPN IP", "kind": "signal", "weight": 15, "group": "network", "category": "network"}],
  "signals": {"ip": {"address": "203.0.113.7", "country": "SG", "asn": 7473, "datacenter": false, "vpn": true, "tor": false, "relay": false},
              "client": {"kind": "browser", "label": "chrome", "ja4": "t13d…", "ua_family": "chrome"},
              "probe": "ok", "device": {"present": true, "id": "d1.…", "new": false, "users": 1}},
  "token_status": "valid", "user": {"id": "3f9a1c…", "attributes": {"api_calls_total": 0}},
  "action": "topup", "test": false, "created_at": "2026-09-27T10:00:00Z" }
alan
id Verdict kimliği. Kaydedin; geri bildirim için kullanın.
flow_id Bu verdict'in ait olduğu deneme (fl_…); bir doğrulamadan sonraki tekrarlar aynı akışı paylaşır.
decision allow · review · deny · challenge. Bu alana göre davranın.
would_decision, would_challenge Eşleşen kuralların hepsi (İzleniyor durumundakiler dahil) uygulansaydı verilecek karar; bu bir doğrulama olacaksa would_challenge {mode, checks} olur ("bunlardan biri" için: adaylar). İzleniyor durumundaki hiçbir kural eşleşmediyse null.
decided_by {type, id?, name?, code}: kararı neyin verdiği — bkz. kararlar.
challenges decision challenge olduğunda: şimdi çalıştırılacak 1–3 doğrulama adımı (birden fazlası yalnızca doğrulamaları "hepsi aynı anda" isteyen bir kuralda); her biri id, check (anahtar), name, kind (widget · app), provider, expires_in (sn), reissued taşır. Widget doğrulamaları ayrıca site_key, mode, action, nonce, script_url, timeout_ms, ui (doğrulama penceresinin görünümü) ve SDK widget'ı gösterdiğinde appearance: "visible" ekler (Friendly Captcha AB: region: "eu"; abusend'in kendi doğrulamaları: params, site_key veya script_url yok — bkz. abusend'in kendi doğrulamaları). Aksi halde [].
risk {score, level} — bir ipucu. Geçerli token yoksa level unknown olur.
reasons Neden: kararı veren kural veya yerleşik kural, doğrulama sonuçları, İzleniyor durumundaki kuralların eşleşmeleri (monitor: true), ardından riske katkı veren sinyaller. Bkz. reason kodları.
signals Öncü sinyallerin özeti (IP, istemci, prob durumu, cihaz).
token_status valid · missing · expired · invalid · project_mismatch · reused · user_mismatch.
user Kullanılan kullanıcı (saklanan öznitelikler bu çağrınınkilerle birleştirilmiş).
test Test projelerinde true — üretimde false olduğunu doğrulayın.
retry_after_seconds Yalnızca bir limit karar verdiğinde (check_rate_limited: 3600, challenge_reused: 5).

Kararlar ve kararı kimin verdiği

karar panel ne yapmalı
allow İzin ver devam edin
review Karar uygulamanızda sizin seçiminiz: devam edin, kendi kademeli doğrulamanızı ekleyin veya bekletin — önce decided_by alanını okuyun
deny Engelle genel bir mesajla reddedin; id + reasons kaydedin, istemciye asla göstermeyin
challenge ‹doğrulama› ile doğrula 428 ile yanıtlayın; SDK doğrulamaları çalıştırıp tekrar dener

decided_by.type her review'u netleştirir:

type kararı veren tipik kodlar
builtin kurallarınızdan önce yerleşik bir kural: engel/izin listeleri, yeniden kullanılan bir doğrulama adımı blocklist_user, blocklist_ip, blocklist_device, allowlist_*, challenge_reused
token token: kullanılamayan bir token (deny, her zaman) veya token zorunlu bir işlemde eksik token — token_missing = deny reddeder; review (varsayılan) kurallarınızın izin vereceği durumu review'a çevirir token_expired, token_invalid, token_missing, …
rule kurallarınızdan biri (id, name) kuralın anahtarı veya rule_<id>
check bir doğrulama adımının sonucu: on_fail, on_incomplete, on_error veya hâlâ bekleyen bir adım challenge_failed, challenge_incomplete, challenge_error, check_unavailable, challenge_required
limit bir limit: gönderim sınırı veya bir denemenin alabileceği doğrulama sayısı (decided_by.name: sığmayan kural) check_rate_limited, challenge_limit
default uygulanan hiçbir kural eşleşmedi (İzleniyor durumundaki kurallar yalnızca raporlar, bkz. would_decision) no_rule_matched
test test projesinin zorunlu kararı veya simulate test_forced

Kurallarınızdan önce (her zaman, kuralların durumundan bağımsız): kullanılamayan token'lar (expired, invalid, project_mismatch, reused, user_mismatch) ve engel listeleri → deny. token: required bir işlemde eksik token → işlemin token_missing ayarı: deny burada biter; review (varsayılan) kurallarınızın çalışmasına izin verir (token göndermemek hiçbir doğrulamayı atlatmaz) ve onların allow kararını review'a çevirir. Kurallarınız sonra yukarıdan aşağı çalışır: uygulanan herhangi bir deny kazanır; bu akışta başarısız olan bir doğrulama → onun on_fail ayarı (birden fazlaysa en katısı); aksi halde ilk uygulanan allow, review veya challenge(doğrulama grubu) karar verir (doğrulamaları akışta zaten geçilmiş veya hatırlanan bir grup atlanır — "bunlardan biri" için geçilmiş tek doğrulama yeter); hiçbir şey eşleşmezse → allow. İzin listeleri en üstteki allow kurallarıdır: doğrulama kurallarınızı atlarlar ama bir deny'ı asla yenmezler.

Bir şeyi saklayan istemcinin (token, çözüm, challenge kimliği) alabileceği en iyi sonuç, ayarlanmış eksik / tamamlanmamış sonucudur — asla allow değil.

Review'u ele almak: onReview

review, kararın uygulamanızda olduğu anlamına gelir. Node SDK'sının guard() ve protect() fonksiyonları onReview ister; böylece bir review asla sessizce geçmez. Seçiminiz review_handling olarak gönderilir (panelde işlemde gösterilir; kararı asla değiştirmez):

onReview bir kuralın veya token zorunluluğunun verdiği review (decided_by.type rule, token) bir doğrulamanın, limitin veya kesintinin verdiği review (check, limit, outage, …)
"continue" devam (guard() null döndürür, protect() next() çağırır) 403 {"error": "verification_incomplete", "retry_after_seconds"?}
"block" 403 {"error": "forbidden"} 403 {"error": "verification_incomplete", …}
bir fonksiyon guard(): (request, verdict) => Response | null; protect(): (req, res, verdict, next) — yanıtınız ya da devam için null / next() aynı fonksiyon (önce decided_by alanını okuyun)

Yani "continue" ile SMS kodunu hiç girmeyen biri geçemez. Bir limit karar verdiğinde retry_after_seconds ayarlanır (ör. check_rate_limited: "bir saat sonra tekrar deneyin"). SDK olmadan aynı kuralı kendiniz uygulayın: bir review'a yalnızca decided_by.type rule veya token olduğunda devam edin.

428 sözleşmesi

protect() / guard() fonksiyonlarının döndürdüğü, Abusend.fetch fonksiyonunun anladığı ve yerel uygulamaların uyguladığı yanıt:

HTTP/1.1 428 Precondition Required
Abusend-Challenge: 1
Content-Type: application/json

{"error": "challenge_required", "challenges": <verdict.challenges>}

challenges 1–3 doğrulama adımı listeler; istemci hepsini çalıştırır (aynı anda olabilir: widget: script_url adresinden, doğrulaması POST /v1/challenges/{id}/verify ile; uygulama: sizin arayüzünüz, sonucu sunucunuz bildirir) ve hepsi tamamlanınca asıl isteği yeni bir token ve X-Abusend-Challenge: <id>,<id> ile — 428'deki her kimlik, virgülle ayrılmış — tekrarlar. Tekrar, sonraki adım için bir 428 daha alabilir (akış başına en fazla projenin doğrulama bütçesi kadar adım, varsayılan 3). Hâlâ bekleyen bir adım, sonucu gelene kadar reissued: true ile geri döner. Abusend.fetch 5 turdan sonra (3.2 öncesi SDK'lar: 3), bir handler reject ettiğinde, bir uygulama doğrulaması için handler kayıtlı değilse veya bir widget yüklenemezse vazgeçer: o turun diğer uygulama handler'larını durdurur, bitmeyen her adımı tamamlanmadı olarak bildirir ve 428 yanıtını kodunuza döndürür — "Bu isteği doğrulayamadık, lütfen tekrar deneyin" gösterin.

Kural koşulları, alanlar ve yardımcılar

Koşullar expr-lang ifadeleridir (≤ 2000 karakter, ≤ 500 düğüm, proje başına ≤ 200 kural). Paneldeki cümle oluşturucu bunları sizin için yazar; ifade sekmesi tipli alanları gösterir. Gruplar, oluşturucudaki sırayla:

grup alanlar
Bağlam context.<anahtar> (tanımladığınız bağlam öznitelikleri)
Kullanıcı user.present, user.id, user.age_days (abusend'in kullanıcıyı ilk gördüğünden beri), user.new_device, user.devices_30d, user.<anahtar> (tanımladığınız öznitelikler), passed("sms")
Olaylar event_count("topup", "1h"), event_sum("topup", "24h"), event_last("topup") — sunucunuzun bildirdiği olaylar (olaylar); attempts("topup", "5m") — kullanıcının bir işlemi deneme sayısı; counter("payments_by_country", "1h") — tüm kullanıcıların toplamı (ortak sayaçlar)
Hız user.verdicts_1h, user.challenges_issued_1h, ip.assessments_1h, device.assessments_1h, device.users_24h, device.challenges_not_passed_1h, device.challenges_issued_1h, network.challenges_not_passed_1h, network.challenges_issued_1h, fingerprint.devices_1h
Ağ (ülke dışında ipucu) ip.country, ip.asn, ip.org, ip.datacenter, ip.vpn, ip.tor, ip.relay, ip.address, ip.score
Cihaz device.present, device.id, device.age_seconds, device.users_total, device.denies_24h, device.blocked_user_seen, device.challenge_passed_recently, device.recognized, device.recognition (high · medium · ""), device.recognized_blocked, device.recognized_users (tanıma)
Tarayıcı / TLS (ipucu) probe.status (ok · absent · failed · none), client.kind, client.label, client.ua_family, client.ua_mismatch, client.h2_mismatch, client.ua_mobile, client.ja4, client.origin, header(name), js.webdriver, js.headless, js.headless_gpu, js.fingerprint_randomized, js.cdp_detected, js.interactions, js.timezone
Risk (ipucu) risk.level, risk.score
İstek action, token.status (valid · missing), checks.ip (match · mismatch · unknown · not_checked), checks.action

network.* istemcinin IPv4 /24 veya IPv6 /48 bloğudur — paylaşılan ağlara (CGNAT, ofisler) dikkat edin. Doğrulama adımlarından gelen sayaçlar yalnızca bir kural onları kullandığında hesaplanır.

Yardımcılar: days_since(ts), hours_since(ts), has(x), in_list("liste_anahtari", değer), cidr(ip.address, "203.0.113.0/24"), header("sec-fetch-site"), now() (Unix saniye), passed("doğrulama_anahtarı") (bu akışta geçildi veya hatırlandı), email_domain(user.email) (küçük harfli alan adı; adres değilse "").

ip.country == "SG" && user.api_calls_total == 0 && context.amount >= 100     → SMS ile doğrula
days_since(user.registered_at) < 7 && context.amount >= 500                  → SMS ile doğrula
ip.country != "" && user.country != ip.country                               → SMS ile doğrula
device.present && device.users_24h > 3                                       → Karar uygulamanızda
event_count("topup", "1h") >= 5                                              → SMS ile doğrula
ip.datacenter && !ip.relay                                                   → captcha ile doğrula (kayıt)
risk.level in ["high", "critical"]                                           → captcha ile doğrula

Eksik değerler

Doğrulamalar (checks)

Doğrulamaları Doğrulamalar altında bağlayın (proje başına birden fazla). Widget doğrulamaları sağlayıcının site anahtarını ve gizli anahtarını ister (şifreli saklanır, bir daha gösterilmez; Test sağlayıcıya karşı kontrol eder); abusend'in kendi doğrulamaları ise ikisini de istemez (gizli anahtar reddedilir). Uygulama doğrulamalarının bizim tarafımızda gizli anahtarı yoktur.

ayar anlamı
key kuralların ve SDK handler'larının kullandığı kısa ad (captcha, sms); proje içinde benzersiz (check_key_taken)
provider widget: turnstile · recaptcha (v3, v2_invisible veya v2_checkbox) · recaptcha_enterprise (+ Google Cloud proje kimliği) · hcaptcha · friendly_captcha (site anahtarı + API anahtarı; region global veya eu) · geetest (v4: site anahtarı = 32 karakterlik captcha_id, gizli anahtar = captcha_key) · abusend_pow · abusend_hold · abusend_trace (abusend'in kendi doğrulamaları: anahtar yok); uygulama: sms · email · kyc · custom
appearance widget: invisible (varsayılan: widget arka planda çalışır, yalnızca sağlayıcı bir etkileşim istediğinde görünür) veya visible (tarayıcı SDK'sı widget'ı — onay kutusu veya bulmaca — kendi doğrulama penceresinde gösterir). Turnstile, hCaptcha, Friendly Captcha ve GeeTest ikisini de destekler; reCAPTCHA v2_checkbox her zaman görünürdür (onay kutusu site anahtarı gerekir); reCAPTCHA v3 / v2_invisible ve Enterprise yalnızca görünmezdir. abusend_pow yalnızca görünmez, abusend_hold ve abusend_trace yalnızca görünürdür. Görünür bir doğrulama adımı kişiyi doğrulamanın timeout_seconds süresi kadar bekler.
threshold reCAPTCHA v3 / Enterprise: altındaki skor başarısız olur (varsayılan 0.5)
pow abusend'in kendi doğrulamaları: bulmaca boyutu {mode, fixed, low, medium, high, critical} — mode risk (verdict'in risk seviyesine göre; varsayılan 15 / 18 / 20 / 22), fixed (varsayılan 18) veya off (yalnızca hold ve trace); boyutlar 10–26, her adım işi ikiye katlar
strictness abusend_hold, abusend_trace: lenient · normal (varsayılan) · strict — bir denemenin ulaşması gereken hareket puanı (0.3 · 0.5 · 0.7)
remember_seconds 0–604800 (7 gün): bir başarı bu kadar süre hatırlanır (varsayılan widget 24 sa / uygulama 0); panelde saniye, dakika, saat veya gün olarak ayarlanır. remember_hours (tam saat, aşağı yuvarlanır) hâlâ döner ve kabul edilir
on_fail deny; review yalnızca skor tabanlı widget'larda (reCAPTCHA v3 / Enterprise; v2 değil)
on_incomplete tamamlanmadı — bırakıldı, süresi doldu, çalışamadı, gönderim sınırına ulaşıldı: review (widget varsayılanı: reklam engelleyiciler gerçek kullanıcıları etkiler) · deny (uygulama varsayılanı: gerçek kullanıcı tekrar dener)
on_error sağlayıcı doğrulayamadı (kesinti, açık devre kesici, okunamayan gizli anahtar): review (varsayılan) · deny
timeout_seconds widget 30–300 (120) · uygulama 60–1800 (600)
max_issued_per_hour yalnızca uygulama, 1–100 (5): gönderim sınırı

Hiçbir on_* ayarı allow olamaz. Bir kuralın (tek başına veya bir grupta) kullandığı doğrulama silinemez (check_in_use). Widget doğrulamalarını tarayıcı SDK'sı çalıştırır, edge doğrular (POST /v1/challenges/{id}/verify); tarayıcı her iki türdeki bekleyen bir adımı da tamamlanmadı olarak bildirebilir (script_blocked, timeout, widget_error, abandoned) — asla geçti olarak değil. hCaptcha'nın ve GeeTest'in kendi işlem bağı yoktur (GeeTest'in yanıtında host adı da yoktur); doğrulamaları adımın cihaz çerezine ve izinli origin'lerinize dayanır. Friendly Captcha'nın yanıtı widget'ın çalıştığı origin'i taşır; bu origin izinli origin'lerinizden biri olmalıdır. GeeTest v4 için tarayıcı SDK'sı widget'ın getValidate() nesnesini provider_token olarak gönderir (lot_number, captcha_output, pass_token, gen_time alanlarıyla sıkıştırılmış JSON); edge onu captcha_key'inizle imzalar. Doğrulama penceresinin (görünür doğrulamalar) görünümü bir proje ayarıdır, settings.challenge_ui: theme (auto · light · dark, varsayılan auto), accent (#rrggbb veya boş) ve branding (küçük bir "Protected by abusend" satırı, varsayılan açık).

Uygulama sonuçlarını sunucunuz gizli anahtar ve kullanıcı kimliğiyle bildirir:

POST /v1/challenges/{id}/result   Authorization: Bearer sk_…
{"status": "passed" | "failed" | "abandoned", "user_id": "3f9a1c…"}   →   {"status": "passed"}

İlk sonuç kazanır; aynı sonucu tekrar göndermek idempotenttir; farklı bir sonuç → 409 challenge_resolved. Yanlış kullanıcı → 409 challenge_user_mismatch (kullanıcısız verilmiş bir adım user_id almaz). Widget adımı → 409 challenge_kind_mismatch. Bildirilmeyen uygulama adımları süre dolunca incomplete olur. Doğrulamalar sayfası uygulama doğrulamalarını ilk sonuç gelene kadar "İlk sonuç bildirimi bekleniyor" olarak ve verilip hiç bildirilmeyen adımların oranını gösterir.

Doğrulama penceresi

Bir kişinin doğrulama yapması gerektiğinde — görünür bir kutu ya da bulmaca (appearance: "visible", reCAPTCHA v2 onay kutusu) veya etkileşimli hâle gelen bir arka plan doğrulaması — tarayıcı SDK'sı (3.1+) bunu küçük bir pencerede gösterir: soluklaştırılmış sayfanın üstünde bir kart (telefonlarda alttan açılan bir panel); başlık, sağlayıcının widget'ı, Vazgeç (Escape gibi abandoned olarak bildirilir) ve kapanmadan önce kısa bir "Doğrulandı". Erişilebilirdir (etiketli bir modal pencere, odak içeri alınır ve geri verilir, azaltılmış hareket tercihine uyulur), bir Shadow DOM içinde yalıtılmıştır ve CSP değişikliği gerektirmez (stilleri oluşturulabilir bir stil sayfasıdır; eski tarayıcılar betiğin nonce'uyla bir <style> alır). Metinler sayfanın <html lang> değerini izler: İngilizce, Türkçe, Almanca, Fransızca, İspanyolca, İtalyanca, Portekizce ve Felemenkçe.

Görünümü settings.challenge_ui ayarıdır (panel → Proje ayarları → Doğrulama penceresi, önizlemeyle); bir sayfa bunu değiştirebilir ya da kapatıp kendi arayüzünü çizebilir:

Abusend.configure({ ui: { theme: "dark", accent: "#16a34a", lang: "tr", branding: false, text: { title: "Son bir adım" } } });
Abusend.configure({ ui: false });                        // kişi gerektiren widget'lar: küçük köşe kutusu
Abusend.configure({ challengeContainer: "#verify-here" }); // kendi arayüzünüz; ne zaman olduğunu challengeStatus() söyler

Betik etiketinde: data-ui-theme, data-ui-accent, data-ui-lang, data-ui="off". 3.1 öncesi SDK'lar appearance ve ui alanlarını yok sayar: görünür bir Turnstile veya hCaptcha doğrulaması bu durumda arka planda çalışır (yalnızca sağlayıcı isterse görünür); tanımadıkları modlar (recaptcha_v2_checkbox, friendly_captcha, geetest_v4) widget_error olarak bildirilir — doğrulamanın "doğrulanamadığında" sonucu, asla İzin ver değil; 3.3 öncesi SDK'lar abusend_pow, abusend_hold ve abusend_trace için de aynısını yapar. Bunları kullanmadan önce SDK'yı güncelleyin.

Doğrulama grupları

Bir ‹doğrulama› ile doğrula kuralı bir doğrulama grubu ister: 1–3 farklı doğrulama ve bunların nasıl birleşeceği. Tek doğrulamalı bir kural, tek elemanlı bir gruptur.

mod panel ne olur
any Bunlardan biri (rastgele, ağırlığa göre) Verildiği anda ağırlığa göre rastgele seçilen bir doğrulamanın tek adımı (ör. Turnstile 70 / hCaptcha 30). Onu geçmek kuralı karşılar. Kullanılamayan bir doğrulama (açık devre kesici, eksik veya okunamayan gizli anahtar) atlanır; ağırlık 0 = yalnızca yedek, ağırlıklı her doğrulama kullanılamadığında kullanılır; hiçbiri kullanılamıyorsa → ilk doğrulamanın on_error ayarı.
sequence Hepsi, sırayla Sırayla, her seferinde bir adım; biri geçilince sıradaki (her biri için bir 428). Hepsi geçildiğinde karşılanır.
parallel Hepsi aynı anda Tüm doğrulamalar aynı 428'de (challenges hepsini listeler). Hepsi geçildiğinde karşılanır.

Akışlar, yeniden denemeler ve challenge kimlikleri

Hatırlanan başarılı doğrulamalar

remember_seconds > 0 iken yakın tarihli bir başarı, bir doğrulama kuralının atlanmasını sağlar:

Gönderim sınırı (SMS pumping)

Doğrulamanın max_issued_per_hour değerine (varsayılan 5) kullanıcı için, yoksa cihaz için, yoksa ağ (IPv4 /24 · IPv6 /48) için ulaşıldıysa bir uygulama adımı verilmez. Verdict doğrulamanın on_incomplete ayarıyla, decided_by.type = "limit", kod check_rate_limited ve retry_after_seconds: 3600 ile yanıtlanır — kullanıcıya daha sonra denemesini söyleyin. Yeniden dönen adımlar kancanızı asla yeniden çalıştırmaz. SMS pumping şablonu (network.challenges_issued_1h >= 20 → Engelle) ağ genelinde bir fren ekler.

Önce izle, sonra uygula

Bir kuralın durumu Kapalı, İzleniyor veya Uygulanıyor olur ve izleme/uygulama arasındaki tek anahtar budur: Uygulanıyor durumundaki bir kural kapsamındaki her işlemde — abusend'in ilk kez gördüğü bir işlem dahil — karar verir; İzleniyor durumundaki bir kural yalnızca ne yapacağını raporlar. İşlemlerin modu yoktur.

  1. SDK'yı ve verdict çağrısını yayına alın. İşlemler kullanıldıkça görünür. Her projenin başladığı iki önerilen kural (Tekrarlanan başarısız veya tamamlanmamış doğrulamalar → Engelle, Yüksek risk → Karar uygulamanızda) İzleniyor durumundadır; uygulanan bir kural yokken verdict'ler allow döner, İzleniyor durumundaki kuralların ne yapacağı would_decision / would_challenge alanlarındadır. Yerleşik deny'lar (kullanılamayan token'lar, engel listeleri) ve token zorunluluğu her zaman uygulanır.
  2. Kuralları İzleniyor durumunda kaydedin (varsayılan). Trafik ("olacaktı: SMS ile doğrula") ve Genel bakış (sürtünme hunisi: hiç doğrulama görmeyen, doğrulanan, geçen, bırakan, başarısız olan, engellenen kullanıcılar) sayfalarını izleyin. unseen_attributes listeleyen bir kural, sunucunuzun hiç göndermediği bir özniteliği okuyordur: önce entegrasyonu düzeltin.
  3. "Karar uygulamanızda" durumunu bilinçli olarak ele alın (onReview); Genel bakış, verdict'lerin çoğu review ile biten işlemleri işaretler.
  4. Olacak eşleşmeleri doğru göründüğünde bir kuralı uygulayın (onay iletişim kutusu oluşturulduğundan beri izleme eşleşmelerini gösterir; isteğe bağlı "Ne yapardı?" bölümü açıldığında bir yeniden oynatma çalıştırır). Yapılacaklar bunu önerilen kurallar için önerir.
  5. Tekrar gözden geçirin: verdict'lere geri bildirim (fraud / legit), her değişiklikten önce yeniden oynatma.

Yeniden oynatma ve simülasyon

Verdict'ler girdilerini saklar (kullanılan kullanıcı öznitelikleri, bağlam, sayaçlar ve akış durumu); saklanan bir verdict, o an geçerli kurallarla yeniden değerlendirildiğinde aynı kararı üretir. Yeniden oynatma (kural düzenleyici, MCP simulate_rule) son 7 günün akışlarının ilk verdict'lerini (en yeni 20 000) kural uygulanıyormuş gibi, değişikliğinizden önce ve sonra, güncel listeler ve doğrulamalarla çalıştırır; eşleşen akışları, değişen kararları ve doğrulama başına günlük kullanıcı sayısını raporlar (henüz bağlanmamış bir doğrulama "‹SMS — bağlı değil› ile doğrula" olarak görünür). Kuralın kullandığı bir alan verdict'lerin çoğunda eksikse uyarır. En az örneklem yoktur ve test projesi trafiği de sayılır: henüz trafik yoksa düzenleyici Deneyin formunu sunar (kurallarınız ve yeni kural, yazdığınız öznitelik ve bağlamla uydurma bir istek üzerinde); 200 verdict'in altında oran yerine sayı gösterir ("37 karardan 3 tanesiyle eşleşirdi") ve eşleşen her akışı listeler. Doğrulama kuralları ilk adımlarına kadar yeniden oynatılır. Saklanan bir verdict gerçekte verdiği doğrulamayı raporlar; yeni bir "bunlardan biri" kuralı doğrulamalarından "biri" olarak raporlanır ve doğrulama adımları ağırlığa göre paylaştırılır (beklenen sayılar).

Test

Test projeleri (pk_test_… / sk_test_…):

Cihaz kimliği

Gizli pencerelerde tanıma (isteğe bağlı). privacy.device_id = recognition ile (Kurulum → Gizlilik → Gizli pencerelerde cihazları tanı) cihaz kimliği göndermeyen bir masaüstü tarayıcı — gizli pencere, temizlenmiş depolama — bilinen bir cihaza bağlanabilir. Tanıma bağlar, asla birleştirmez: tarayıcı her zaman kendi yeni cihaz kimliğini ve çerezini alır.

Proxy arkasında istemci IP'si

Gerçek istemci IP'sini client_ip olarak gönderin; abusend bunu token'ı isteyen IP ile karşılaştırır (checks.ip, sinyal ip_mismatch) ve token'sız verdict'lerde IP verisi, ağ sayaçları ve adillik için kullanır.

kurulum istemci IP'si
proxy yok soket karşı tarafı (req.socket.remoteAddress, $_SERVER['REMOTE_ADDR'], r.RemoteAddr)
Cloudflare CF-Connecting-IP
yük dengeleyici / ters proxy X-Forwarded-For içinde sizin proxy'lerinizden olmayan en sağdaki giriş
Express app.set("trust proxy", <atlama sayısı veya CIDR'ler>), sonra req.ip (protect() bunu gönderir)
Fetch API (guard()) varsayılan olarak X-Forwarded-For içindeki en sağdaki giriş, yoksa X-Real-IP; istemciler uygulamaya proxy'niz olmadan ulaşabiliyorsa clientIp: (request) => … verin

Bu başlıklara yalnızca kendi proxy'lerinizden geldiğinde güvenin. Emin değilseniz yanlış bir değer göndermek yerine client_ip alanını göndermeyin. Aynı IPv4 /24 veya IPv6 /64 eşleşme sayılır; IPv4'e karşı IPv6 unknown olur.

Token'lar, yeniden denemeler ve tekrar

Hata politikası ve gecikme

Kullanıcılar, geri bildirim ve silme

Hepsi Authorization: Bearer sk_… ile:

çağrı
GET /v1/users/{id} {id, attributes, first_seen_at, last_seen_at, recent: [{verdict_id, flow_id, created_at, action, decision, decided_by}]} (404 user_not_found); recent verdict kaydından okunur, verdict çağrısının yaklaşık bir saniye gerisindedir
PATCH /v1/users/{id} {"attributes": {…}} öznitelikleri birleştirir (kullanıcıyı oluşturur); null bir anahtarı siler
DELETE /v1/users/{id} silme (idempotent): {"ok": true, "erased": true | false}; kullanıcının olayları da silinir. 200, silmenin kaydedildiği ve ana veritabanındaki kısmının bittiği anlamına gelir; analitik depodaki ve Valkey'deki kısımlar aynı çağrıda denenir, biri başarısız olursa ya da hâlâ sürüyorsa arka planda biter (garantilidir, genellikle bir dakika içinde: abusend başarılı olana kadar yeniden dener) — kişiye tamamlandığını söylemeden önce bir dakika bekleyin. Bkz. PRIVACY.md
POST /v1/feedback {"verdict_id", "label": "fraud" | "legit", "note"} verdict'in tüm akışını etiketler (404 verdict_not_found)
POST /v1/events {"events": [{user_id, type, value?, properties?, occurred_at?, id?}]} olay bildirir (1–100, ya hep ya hiç): {"accepted", "duplicates"} — bkz. olaylar

Yollardaki kimlikleri URL kodlayın. Node: abusend.users.*, abusend.feedback({ verdictId, label, note }), abusend.events.track(…) / trackMany([…]).

Reason kodları

reasons[] girdileri: code, label, kind (rule · builtin · signal · challenge · info), effect (bir kuralın veya yerleşik kuralın istediği sonuç), weight ve group (sinyaller), rule_id (kurallarınız), monitor: true (eşleşen, izlemedeki bir kural) ve category (automation, network, browser, device, account, token, challenge, lists, custom). Kurallarınız kendi anahtarlarını veya rule_<8 hex> kullanır. Anahtarlar aşağıdaki kodları kullanamaz ve sdk_, test_, monitor_, rule_, token_, system_, challenge_, check_ veya builtin_ ile başlayamaz. GET /api/panel/v1/meta/reason-codes hepsini listeler.

Yerleşik kurallar ve akış kodları:

kod tür ne zaman
blocklist_user, blocklist_ip, blocklist_device builtin (deny) kullanıcı / IP / cihaz bir engel listesinde
allowlist_user, allowlist_ip, allowlist_device builtin (allow) bir izin listesinde (en üstteki allow kuralları; asla bir deny'ın üstünde değil)
token_expired, token_invalid, token_project_mismatch, token_reused, token_user_mismatch builtin (deny) kullanılamayan token (her zaman)
token_missing builtin token zorunlu işlemde token yok: deny (token_missing = deny) veya kurallarınızın izin vereceği yerde review (review)
challenge_reused builtin (deny) tüketilmiş bir doğrulama adımı yeniden gönderildi (5 sn tekrarı değil)
challenge_required challenge bir doğrulama adımı verildi veya hâlâ bekliyor
challenge_passed, challenge_remembered challenge bu akışta geçildi / hatırlanan başarı
challenge_failed challenge başarısız → doğrulamanın on_fail ayarı
challenge_incomplete challenge tamamlanmadı → on_incomplete
challenge_error challenge sağlayıcı doğrulayamadı → on_error
check_unavailable challenge doğrulama şu an çalışamıyor (ayarlanmamış, gizli anahtar okunamıyor, devre kesici açık) → on_error
check_rate_limited challenge gönderim sınırına ulaşıldı → on_incomplete
challenge_limit challenge eşleşen kurallar projenin deneme başına izin verdiğinden fazla doğrulama istiyor → review
challenge_invalid, challenge_attached info gönderilen kimlik kredi vermedi / önceki bir adım arama ile eklendi
test_forced info test projesi geçersiz kılması
no_rule_matched info hiçbir şey eşleşmedi → allow

Sinyaller (risk değerini besleyen ipuçları; bir grupta yalnızca en büyük ağırlık sayılır; ağırlıklar ve açık/kapalı durumu proje başına Sinyaller altında değiştirilebilir):

kod grup ağırlık ne zaman
client_bot_library tls_consistency 50 TLS el sıkışması bilinen bir HTTP kütüphanesi veya aracıyla eşleşiyor
ua_mismatch tls_consistency 45 User-Agent, TLS parmak izinin çeliştiği bir tarayıcı iddia ediyor (birden çok tarayıcının paylaştığı bir el sıkışma, client.label chrome/firefox, hiçbiriyle çelişmez)
h2_mismatch tls_consistency 40 bir araca veya başka bir tarayıcıya ait HTTP/2 ayarları
no_grease_chromium tls_consistency 20 GREASE göndermeyen Chrome/Edge
client_unknown tls_consistency 15 TLS parmak izi katalogda yok (inceleyen proxy'ler, nadir tarayıcılar)
sec_fetch_missing automation 25 Sec-Fetch-* olmadan Chrome/Edge/Firefox User-Agent'ı
js_webdriver automation 60 navigator.webdriver true
js_headless automation 40 headless özellikleri
js_ua_mismatch automation 25 navigator.userAgent başlıktan farklı
js_inconsistent automation 15 pencere, ekran, dokunma veya dil özellikleri çelişiyor
no_interaction automation 10 login/register/signup/checkout öncesi etkileşim yok
header_anomaly automation 10 Accept veya Accept-Language eksik
js_headless_gpu automation 50 yazılımsal WebGL işleyicisi
js_clean_dirty_mismatch automation 40 bir özellik temiz bir iframe ile sayfa arasında farklı
js_native_tostring_tampered automation 35 yerel bir fonksiyonun toString sonucu yerel değil
js_navigator_overridden automation 35 bir navigator getter'ı yeniden tanımlanmış
js_cdp_detected automation 55 DevTools protokolü izleri
js_webdriver_advanced automation 55 navigator.webdriver ötesinde otomasyon işaretleri
js_probe_tampered automation 60 probun bütünlük kontrolleri başarısız
js_ua_platform_mismatch automation 25 navigator.platform User-Agent ile çelişiyor
js_screen_anomaly automation 15 imkânsız ekran geometrisi
fingerprint_randomized automation 10 canvas / ses parmak izleri aynı çizimde değişiyor (gizlilik tarayıcıları, gizli modlar, anti-detect araçları); tanınmaz
probe_failed probe 80 bir prob gönderildi ama açılmadı (tekrar oynatılmış, değiştirilmiş, süresi dolmuş)
probe_absent probe 80 token, SDK'nın probu olmadan istendi
ip_tor network 40 Tor çıkış düğümü
ip_high_risk network 25 üçüncü taraf IP skoru ≥ 75 (varsayılan kapalı)
ip_datacenter network 25 barındırma / bulut adresi (iCloud Private Relay değil)
ip_vpn network 15 ticari VPN
ip_velocity network 20 önceki saatte bu IP'den > 30 token isteği
ip_mismatch consistency 20 client_ip token isteğinden farklı bir /24 · /64 içinde
action_mismatch consistency 20 token başka bir işlem için istendi

risk.score toplamdır (0–100); varsayılan eşiklerle risk.level low < 25 ≤ medium < 50 ≤ high < 75 ≤ critical. Her proje, son trafiğine etkisini önizledikten sonra eşikleri değiştirebilir (1 ≤ medium < high < critical ≤ 100); seviyeler yalnızca kurallarınız için puan aralıklarına ad verir ve daha önce verilmiş kararlar aldıkları seviyeyi korur. Başarısız bir prob tek başına riski critical yapar, ama yine de bir ipucudur: temel kural (siz uyguladığınızda) yüksek riskte bir widget doğrulaması ister ve geçilmiş bir doğrulama bunu dengeleyebilir. Hiçbir varsayılan kural yalnızca riske dayanarak engellemez (Kritik risk → Engelle katı bir şablondur).

Hatalar

Her hata aynı biçimdedir ve bir X-Request-Id başlığı taşır (destek için belirtin):

{"error": {"code": "challenge_user_mismatch", "message": "The challenge was issued for another user.", "docs": "https://rep.example.com/docs#errors"}}

Edge API:

kod HTTP uç nokta anlamı / çözüm
invalid_json 400 POST/PATCH boş gövde, geçersiz JSON veya yanlış tip — bir JSON nesnesi gönderin (PHP: boş dizileri (object) yapın)
invalid_body 400 POST/PATCH gövde okunamadı
body_too_large 413 POST/PATCH > 16 KiB (assess, feedback), > 192 KiB (verify), > 4 KiB (result), > 64 KiB (verdict, users), > 640 KiB (events)
invalid_site_key 401 assess, verify site anahtarı eksik veya bilinmiyor — paneldeki pk_… değerini kopyalayın; test ve live'a dikkat
origin_not_configured 403 assess, verify izinli origin'i olmayan live proje
origin_not_allowed 403 assess, verify sayfanın origin'ine izin verilmemiş
invalid_action 400 assess, verdict işlem ^[a-z0-9_.-]{1,32}$ değil (verdict'te zorunlu)
invalid_request 400 verify, result, verdict, feedback hatalı ch_… kimliği (challenge_ids içinde de); 3'ten fazla challenge_ids; provider_token, solution ve error alanlarından tam olarak biri değil; abusend'in kendi doğrulamalarından olmayan bir doğrulama için solution, onlardan biri için provider_token ya da 64'ten fazla bulmaca yanıtı; hatalı review_handling; verdict_id olmadan geri bildirim
invalid_status 400 result status passed · failed · abandoned değil
invalid_user 400 verdict, users, result user.id boş veya > 256 karakter; user_id > 256
invalid_attributes 400 verdict, users > 32 anahtar, hatalı anahtar, > 512 karakterlik değer, iç içe değer, context > 4 KB
invalid_client_ip 400 verdict yalın bir IPv4/IPv6 adresi değil
invalid_simulate 400 verdict allow · review · deny değil
invalid_label 400 feedback fraud · legit değil
invalid_events 400 events events yok, boş veya 100'den fazla
invalid_event 400 events bir olay geçersiz (user_id, type, value, properties, occurred_at biçimi, id); details: {index, field} onu adlandırır; partiden hiçbir şey saklanmaz
invalid_occurred_at 400 events occurred_at 5 dakikadan fazla gelecekte veya 30 günden eski; details: {index, field}
event_value_out_of_range 400 events bir olayın value değeri ±1e15'in ötesinde (değerler toplanır; tutarları makul aralıkta, gerekirse küçük birimle gönderin); details: {index, field}; grubun hiçbiri saklanmaz
missing_api_key 401 sunucu API'si Authorization: Bearer … yok
invalid_api_key 401 sunucu API'si bilinmeyen anahtar veya bir pk_ site anahtarı gönderildi
revoked_api_key 401 sunucu API'si anahtar iptal edilmiş — yenisini yayına alın
challenge_not_found 404 verify, result bu projede böyle bir doğrulama adımı yok
challenge_device_mismatch 403 verify adım başka bir tarayıcıya verilmiş (cihaz çerezi)
challenge_resolved 409 verify, result zaten başka bir sonucu var (ilk sonuç kazanır), süresi dolmuş veya deneme hakkı bitmiş
challenge_kind_mismatch 409 verify, result uygulama adımı için sağlayıcı token'ı veya widget adımı için sonuç
challenge_user_mismatch 409 result user_id, adımın verildiği kullanıcı değil
user_not_found 404 users hiç gönderilmemiş veya silinmiş
verdict_not_found 404 feedback bilinmeyen verdict_id (veya saklama süresiyle silinmiş)
rate_limited 429 hepsi bkz. hız limitleri; saniye cinsinden Retry-After. Verdict'te: review, asla allow
key_lookups_paused 429 anahtarlı hepsi bu istemci bir dakikada 30+ farklı bilinmeyen anahtar gönderdi
not_found 404 herhangi bilinmeyen uç nokta (veya prob uç noktaları açık değil)
method_not_allowed 405 bilinen yollar yanlış metot; Allow başlığına bakın
sdk_tarball_unavailable 503 /sdk/v1/*.tgz sunucu SDK tarball'ları olmadan derlenmiş
busy 503 verdict, events servis kısa süreliğine aşırı yüklü (verdict günlüğü kuyruğu dolu kaldı veya ClickHouse yazıcı kuyruğu bir olay grubunu alamadı: grubun hiçbir şeyi kaydedilmedi, yeniden deneyin); Retry-After: 1. Hata politikanız için bir kesintidir: review (veya deny), asla allow değil; SDK'lar zaman bütçesi içinde iki kez yeniden dener, sonra hata kararını döndürür
state_unavailable 503 verdict, assess servisin durum deposuna (token kullanımı, hız pencereleri) ulaşılamadı; Retry-After: 1. Hata politikanız için busy gibi bir kesintidir: review (veya deny), asla allow değil; hiçbir şey tüketilmedi ve token verilmedi
internal_error 500 herhangi sonra tekrar deneyin; X-Request-Id ile desteğe başvurun

Hata olmayanlar: kullanılamayan token'lar ve kullanılamayan (iyi biçimli) challenge_ids değerleri karardır (HTTP 200), asla 4xx değildir.

SDK: verification_incomplete (protect() / guard() tarafından 403, gövde {"error": "verification_incomplete", "retry_after_seconds"?}) — onReview: "continue" altında bir doğrulama adımının veya bir limitin verdiği review. SDK hata ve yedek kodları Node SDK ve tarayıcı SDK'sı README'lerindedir.

Panel API'si (/api/panel/v1; MCP sunucusu ve asistan da bunları gösterir):

kod HTTP anlamı
attribute_undeclared 400 koşul tanımlanmamış bir öznitelik kullanıyor (details: {scope, key})
attribute_in_use 409 tipini değiştirmek veya silmek istediğiniz özniteliği kurallar kullanıyor
invalid_counter 400 panel: bir sayacın tanımı geçersiz (mesaj alanı söyler)
too_many_counters 400 panel: bir projede en fazla 50 sayaç olabilir
counter_in_use 409 panel: silmek istediğiniz sayacı kurallar okuyor
mail_not_configured 409 panel: e-posta kurulu değil: Sistem → E-posta bölümünde SMTP ayarlarını kaydedip açın
mail_send_failed 502 panel: test e-postası gönderilemedi (mesaj sunucunun yanıtını taşır, hiçbir sır içermez)
invalid_smtp_host, invalid_smtp_port, invalid_smtp_security, invalid_smtp_username, invalid_from_address, invalid_from_name, invalid_reply_to 400 panel: geçersiz SMTP ayarları (details.field girdiyi söyler)
smtp_security_refused 400 panel: şifrelenmemiş SMTP (none) yalnızca localhost'a veya geliştirmede kullanılabilir
smtp_password_required 400 panel: sunucuyu, portu veya kullanıcı adını değiştirdikten sonra SMTP şifresini yeniden girin
invalid_webhook, invalid_webhook_url, invalid_webhook_format, invalid_event 400 panel: geçersiz webhook hedefi (herkese açık bir adrese https URL'si, biçim json veya slack, en az bir bilinen olay)
too_many_webhooks 409 panel: bir projede en fazla 5 webhook hedefi olabilir
notifications_unavailable 503 panel: bu kurulumda bildirim servisi yok
check_in_use 409 silmek istediğiniz doğrulamayı kurallar hâlâ istiyor
check_key_taken 409 projenin başka bir doğrulaması bu anahtarı kullanıyor
invalid_check 400 geçersiz doğrulama ayarları (sağlayıcı, anahtar, on_*, süreler, yapılandırma; abusend'in kendi doğrulamaları için bir gizli anahtar)
invalid_rule, invalid_condition 400 geçersiz kural (sonuç, doğrulama grubu — 1–3 farklı doğrulama, mod, ağırlıklar —, kapsam) / derlenmeyen koşul
invalid_rule_key, rule_key_taken 400 / 409 ayrılmış veya yinelenen kural anahtarı
too_many_rules 400 200'den fazla kural
invalid_template, invalid_params 400 bilinmeyen şablon / hatalı şablon parametresi
invalid_action, invalid_attribute, too_many_attributes, invalid_signal, invalid_value 400 geçersiz işlem, öznitelik (tip, birim, etiket; en fazla 200), sinyal ayarı veya değer
invalid_settings, invalid_origins, invalid_environment, invalid_name 400 geçersiz proje ayarları, origin'ler, ortam veya ad
invalid_fix 400 yapılacak işin sunucu tarafında bir düzeltmesi yok
invalid_event_type, too_many_event_types 400 olay tipi ^[a-z0-9_]{1,32}$ değil / 200'den fazla olay tipi listelenemez
invalid_kind, invalid_key, invalid_expiry, system_list 400 listeler: hatalı tür, anahtar veya süre; sistem listeleri silinemez
config_invalid 400 yapılandırma içe aktarma: belgede hatalar var (details.plan.errors)
plan_stale 409 yapılandırma içe aktarma: plandan sonra proje değişti (details.plan yeni bir plandır: gözden geçirin)
enforce_confirmation_required 409 yapılandırma içe aktarma: kurallar uygulanmaya başlayacak; confirm_enforce: true ile uygulayın
invalid_device, invalid_user, invalid_fingerprint, invalid_label, invalid_cursor 400 geçersiz cihaz kimliği, kullanıcı kimliği, parmak izi deseni, geri bildirim etiketi, sayfa imleci
invalid_ip, invalid_since, invalid_until, invalid_sort, invalid_filter, invalid_by 400 kullanıcı, cihaz, ağ ve trafik listeleri: geçersiz IP adresi, zaman aralığı (1h · 24h · 7d · 30d), until zamanı (RFC 3339), sıralama, filtre veya gruplama
forbidden, forbidden_role 403 rolünüz için izin yok (sahipleri yalnızca sahipler yönetir)
last_owner 409 bir organizasyonun bir sahibi olmalı
unauthorized, csrf 401 / 403 oturum açılmamış / X-Abusend-CSRF: 1 eksik
invalid_credentials, invalid_code, mfa_required, mfa_setup_required, mfa_enabled, mfa_not_pending, password_change_required, weak_password 400–409 oturum açma ve 2FA
invalid_email, invalid_role, email_taken, account_exists, invite_invalid, signup_disabled 400–409 ekip ve davetler
confirmation_required 400 bir organizasyonu silmek adını ister
invalid_token, token_not_allowed, too_many_tokens 401 / 403 / 409 kişisel erişim anahtarları
assistant_disabled, assistant_not_configured, assistant_key_unreadable, invalid_base_url, invalid_model, invalid_provider, invalid_max_steps, api_key_required, message_too_long, conversation_busy, no_pending_confirmation 400–409 yapay zekâ asistanı
invalid_recipient 400 panel: Nöbet e-posta alıcıları organizasyonun üyeleri (kullanıcı kimlikleri) olmalıdır
watch_disabled 409 panel: proje için Nöbet kapalı (çalıştırılacak bir şey yok)
watch_owner_missing 409 panel: Nöbet'in adına çalıştığı yönetici artık organizasyonun yöneticisi ya da sahibi değil; devralmak için Nöbet ayarlarını kaydedin
watch_busy 409 panel: projenin bir Nöbet incelemesi zaten çalışıyor
watch_run_limit 429 panel: proje saatlik ya da günlük Nöbet inceleme hakkını kullandı (Retry-After); Nöbet ayarlarındaki sınırları yükseltin ya da bekleyin
watch_unavailable 503 panel: bu kurulumda Nöbet servisi yok
panel_busy 503 panel: bu pod'da aynı anda çok fazla panel isteği var (Retry-After: 1); kısa süre sonra tekrar deneyin. Edge API etkilenmez
conflict 409 eşzamanlı bir değişiklik; yenileyip tekrar deneyin
rate_limited 429 kural testi / simülasyon: proje başına dakikada 10; risk önizlemesi: proje başına dakikada 30; doğrulama önizlemesi (hold / trace): proje başına dakikada 60
unavailable 503 geçici olarak kullanılamıyor

Hız limitleri ve adillik

limit varsayılan aşıldığında
proje + istemci IP'si başına POST /v1/assess (IPv6 /64) 120/dk o istemci için 429; SDK token'sız devam eder
proje + ağ başına POST /v1/assess (IPv4 /24, IPv6 /48) dakikada IP başına limitin 8 katı; günde max(günlük / 50, 1000) yalnızca o ağ için 429
proje başına POST /v1/assess 6000/dk yalnızca bütçeyi harcayan yoğun ağlar 429 alır; diğerleri devam eder (2 katına kadar)
proje başına UTC günü başına POST /v1/assess 200 000 bugün > 100 isteği olan ağlar 429 alır; diğerleri devam eder (2 katına kadar)
gizli anahtar başına verdict 200/sn (ani 400) client_ip ağı bu dakika > 20 verdict yapan son kullanıcılar 429 alır (→ review); diğerleri devam eder (2 katına kadar). client_ip gönderin.
gizli anahtar başına kullanıcı, geri bildirim, doğrulama sonucu 200/sn, verdict'lerden ayrı 429 + Retry-After
gizli anahtar başına POST /v1/events 200 çağrı/sn ve 200 olay/sn (ani 400), verdict'lerden ayrı 429 + Retry-After, partiden hiçbir şey saklanmaz (Node SDK ok: false döner)
istemci IP'si / ağ başına POST /v1/challenges/{id}/verify 60/dk / 480/dk 429 + Retry-After
proje başına POST /v1/challenges/{id}/verify max(assess_per_minute, 300)/dk yalnızca yoğun ağlar 429 alır
istemci başına farklı bilinmeyen anahtar 30/dk 429 key_lookups_paused (kullanımdaki anahtarlar çalışmaya devam eder)

Bir limiti tüketen istemci yalnızca kendi erişimini kaybeder — önce IP'sini, sonra ağ bloğunu. Paylaşılan proje bütçeleri asla herkesi dışarıda bırakmaz. Dört proje limiti proje başına ayarlanır (Kurulum → Limitler). Limitler sunucu örneği başına uygulanır.

Gizlilik notları

Güvenlik kontrol listesi

SSS

abusend botları durdurur mu? Tek başına hayır, ve hiçbir ürün bunu vaat edemez. Sinyaller otomasyonu gizlemeyi pahalılaştırır, doğrulamalar geçmeyi pahalılaştırır; kurallarınız bu maliyetin gerçek bir kullanıcının zahmetine nerede değdiğine karar verir. Her istemci tarafı kontrol aşılabilir — ipuçlarını yalnızca sunucunuzun bildiği gerçeklerle (hesap yaşı, tutar, kullanım) birleştirin.

Reklam engelleyiciler? SDK veya bir widget engellenirse istek token'sız gider ya da doğrulama adımı tamamlanmadı olarak biter. Token zorunlu işlemlerde kurallarınız yine çalışır (eksik token hiçbir doğrulamayı atlatmaz) ve izin verecekleri durum review olur (token_missing = review, varsayılan; deny bunun yerine engeller); widget doğrulamalarının on_incomplete varsayılanı review'dur. Edge'i kendi alan adınızdan sunmak (ör. rep.magazaniz.com) engellemeyi azaltır; abusend'in kendi doğrulamaları başka bir adresten hiçbir şey yüklemez.

VPN, iCloud Private Relay, kurumsal proxy'ler? Bunlar küçük ağırlıklı ipuçlarıdır; Relay bir VPN değildir. TLS inceleyen bir proxy bilinmeyen veya tarayıcı olmayan bir istemci gibi görünür — gerekirse çıkış adresini izinli IP'ler listesine ekleyin.

Tarayıcı bir şey öğrenir mi? /v1/assess yalnızca bir token döndürür. Kararlar, risk ve nedenler yalnızca gizli anahtarınızla görülebilir.

Edge çökerse ne olur? Kullanıcılarınız hata politikanıza göre işlem görür — SDK varsayılanlarıyla review.