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
- Kaydetmek, kuralın kullandığı öznitelikleri tanımlar (
user.api_calls_total,context.amount, ikisi de sayı) ve sunucunuz gönderene kadar "trafikte henüz görülmedi" olarak işaretler. - Kural İzleniyor durumunda kaydedilir: hiçbir karar vermez, ne olacağını kaydeder
(
would_decision: "challenge",would_challenge: {"mode": "any", "checks": ["sms"]}). Trafik sayfası bunu "olacaktı: SMS ile doğrula" olarak gösterir.topupişlemi ilk verdict geldiğinde oluşturulmuştur; yalnızca bir etikettir. - Sunucunuz kuralın okuduğu bir özniteliği gönderene kadar kural onu
unseen_attributesaltında listeler ve kural hiç gelmemiş bir özniteliği okuyor yapılacağı kurala işaret eder: bir kural hiç almadığı bir değerle eşleşemez. - Yeniden oynatma, kuralı son 7 günün trafiği üzerinde kaydetmeden önce ve sonra çalıştırır: eşleşen akışlar, değişen kararlar, SMS doğrulamasını görecek günlük kullanıcı.
- Hazır olduğunuzda: SMS doğrulamasını bağlayın (tarif) ve kuralı Uygulanıyor durumuna alın. Tek anahtar budur: o andan itibaren kapsamındaki her işlemde karar verir.
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:
POST /topup→abusend.verdict({ action: "topup", user, context, clientIp }).decision: "challenge"(challenges: [{check: "sms", …}]) →challengesiçindeki kimlikleri oturuma kaydedin, OTP'yi gönderin,/verifysayfasına yönlendirin./verify→ kullanıcı kodu girer → sunucunuz kodu kontrol eder →abusend.challenges.result(id, "passed" | "failed", { userId }).- Geri yönlendirin ve verdict'i
challengeIds: idsile 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.
- Sunucunuz
guard()/protect()fonksiyonunu her zamanki gibi kullanır; doğrulama,{"error": "challenge_required", "challenges": [{…}]}gövdeli veAbusend-Challenge: 1başlıklı bir 428 olur. - Uygulama
challengesiçindeki her adımı çalıştırır (1–3; birden fazlası yalnızca bir kural doğrulamaları "hepsi aynı anda" istediğinde):check(ör.sms) veiddeğerlerini okur, kendi OTP ekranını gösterir ve kodu sunucunuza gönderir; sunucu sonucuabusend.challenges.result(id, …, { userId })ile bildirir. - Uygulama asıl isteği
X-Abusend-Challenge: ch_…[,ch_…]başlığıyla (428'deki her kimlik, virgülle ayrılmış) tekrarlar;guard()/protect()bunlarıchallenge_idsolarak iletir. - Bir adımdaki
reissued: true, aynı doğrulama adımının hâlâ beklediği anlamına gelir (sonuçtan önce bir tekrar): yeni kod göndermeden OTP ekranını tekrar gösterin. Bu ekranda bir "kodu tekrar gönder" düğmesi sunun: doğrulamayı başlatan yanıt kaybolduysa tekrar deneme zatenreissueddöner ve hiç kod gönderilmemiştir.
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ı
-
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. -
Kural. Sonucu SMS ile doğrula olan herhangi bir kural, ör. hızlı başlangıçtaki şablon.
-
Sunucu: kodu gönderin;
protect()/guard()fonksiyonununchecks.smskancasında. Kanca yalnızca bir doğrulama adımı ilk kez verildiğinde çalışır (challenge.reissuedfalse), yani tekrarlar asla yeniden SMS göndermez. "Kodu tekrar gönder" düğmesi sizin kendi uç noktanızdır. -
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ç }); -
Sunucu: doğrulayın ve bildirin.
failedsonucunu 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 }); }); -
Tekrar. Modal resolve olur,
Abusend.fetchyeni bir token veX-Abusend-Challengeile tekrar dener ve verdict kuralları yeniden çalıştırır: SMS başarılı → sonraki kural veyaallow; başarısız → doğrulamanınon_failayarı (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 |
- 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. - Kural. Her widget gibi ‹doğrulama› ile doğrula. Tarayıcı SDK'sı 3.3.0 veya sonrası
gerekir (eski SDK'lar modu
widget_errorolarak bildirir: doğrulamanın "doğrulanamadı" sonucu, asla İzin ver değil). - 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önderilmesitoken_reusedolarak başarısız olur. - 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: reviewbırakın ya da etkileşim gerektirmeyenabusend_powkullanı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
user.attributeshesap hakkındaki gerçeklerdir; son kullanıcı kaydında saklanır ve her çağrıda birleştirilir (nullbir anahtarı siler).contextbu isteği anlatır (tutar, para birimi, plan) ve yalnızca verdict ile birlikte saklanır.- İkisi de tiplidir:
number,string,boolveyatimestamp(birimms,sveyaiso). Bir öznitelik şu durumlarda tanımlıdır: bir şablon ya da kural oluşturucu kayıt sırasında tanımladığında, Kurulum → Bağlam altında siz ayarladığınızda veya abusend onu trafikte gördüğünde (tip tahmin edilir; tahmini kontrol edin). Tanımlanmamış bir özniteliği kullanan ham bir ifadeattribute_undeclaredile reddedilir (panelde tek tıkla tanımlama sunulur). - Önceden hesaplanmış bir yaş değil, zamanın kendisini gönderin:
registered_at: user.createdAt.getTime()(milisaniye), sonradays_since(user.registered_at) < 7. - Yanlış tipteki bir değer eksik okunur ve öznitelik üzerinde sayılır
("
context.amountbugün 3 kez string olarak gönderildi"); isteği asla başarısız kılmaz. Bir kuralın kullandığı özniteliğin tipini değiştirmek veya onu silmek reddedilir (attribute_in_use). - Limitler: her biri en fazla 32 anahtar; anahtarlar
^[A-Za-z_][A-Za-z0-9_]{0,63}$; değerler string (≤ 512 karakter), sayı, bool veyanull;context≤ 4 KB. Kullanıcı öznitelik anahtarları yerleşik alanlarla çakışamaz (id,present,age_days,verdicts_1h,devices_30d,new_device,device_age_seconds,challenges_issued_1h). - Kimlik değil gerçek gönderin: e-posta adresi, telefon numarası, ad veya T.C. kimlik numarası göndermeyin (gizlilik notları).
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.
POST /v1/events(gizli anahtar):{"events": [{user_id, type, value?, properties?, occurred_at?, id?}]}, çağrı başına 1–100, ya hep ya hiç: tek bir geçersiz olay tüm partiyi reddeder ve hata onu adlandırır (details.index,details.field).user_id: opak kullanıcı kimliğiniz (1–256 karakter); abusend'in hiç görmediği bir kimlik, bir verdict'te olduğu gibi kullanıcıyı oluşturur.value: ±1e15 içinde sonlu bir sayı (yoksaevent_sum'a 0 ekler; daha büyüğüevent_value_out_of_rangeile reddedilir).properties: en fazla 32 skaler değer, 4 KB — saklanır, kurallar okumaz.occurred_at: RFC 3339, varsayılan abusend'in onu aldığı an; en fazla 30 günlük (kurallar en fazla 30 gün geriye bakar) ve gelecekte değil (5 dakikalık saat kayması alındığı an olarak saklanır) — aksi haldeinvalid_occurred_at.id(1–128 yazdırılabilir karakter): kimliği projede zaten bildirilmiş (son 31 gün) bir olay tekrardır —duplicatesiçinde sayılır, yeniden saklanmaz. Kendi yeniden denemeleriniz bir kez sayılsın diye kendi kimliğinizi (ödeme, sipariş) kullanın; siz vermezseniz Node SDK rastgele bir kimlik atar, bu yalnızca SDK'nın kendi yeniden denemelerini korur.- Değerler:
event_count(type, window)kullanıcının bu tipteki,occurred_at'i aralıkta olan olaylarını sayar;event_sum(type, window)onlarınvaluedeğerlerini toplar;event_last(type)30 gün içindeki en yenioccurred_at'tir, yoksa eksiktir (onunla karşılaştırmalar yanlıştır;has(event_last("topup"))bunu sınar). Verdict'te kullanıcı yoksa sayılar ve toplamlar 0'dır. Zaman aralığını siz seçersiniz; dakika, saat veya gün olarak"1m"ile"30d"arası:"5m","90m","2h","3d". - Denemeler için olay gerekmez:
attempts("topup", "5m")kullanıcının bu işlemi aralıkta kaç kez denediğidir, bu istek dahil — kararı ne olursa olsun her verdict çağrısı sayılır;attempts("topup", "5m") > 3→ Engelle, beş dakika içindeki dördüncü yüklemeyi durdurur. Bir doğrulamadan sonraki yeniden deneme aynı denemedir (tekrar sayılmaz). Kullanıcı yoksa 0'dır. - Yalnızca kurallarınızın okuduğu olay tipleri, verdict başına tek sorguda hesaplanır; değerler verdict ile saklanır, böylece yeniden oynatma kuralın gördüğünü gösterir.
- Hiç olayı alınmamış bir tipi okuyan kural henüz çalışamaz: panel sağlık satırında
event.topupgösterir ve henüz çalışamaz yapılacak işini açar. Kurulum → 5. adım alınan olay tiplerini listeler (kural cümlelerinin kullandığı "Yüklemeler" gibi bir etiketle). - Olaylar projenin olay saklama süresi boyunca (varsayılan 90 gün, en fazla 3 yıl; panel → Veri saklama; kurallar son 30 günü okur) saklanır ve kullanıcıyla birlikte silinir (silme). Limitler için hız limitleri.
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
- Sayacın filtresi neyi saydığını seçer; yalnızca o isteklerin etkilenmesini
istiyorsanız koşulu kuralda tekrarlayın (
ip.country == "SG"olmadan ilk kural, Singapur yoğunlaştığında herkese doğrulama sorardı). Gruplama (group_by) her değere kendi toplamını verir —ip.country,email_domain(user.email)— ve kural isteğin grubunu okur. - Olay sayaçları yalnızca
user.*bilir (bir olay istek taşımaz): olayın kullanıcısında olsun diye özniteliği (user.email) verdict'lerinizle veya users API ile gönderin. - Toplamlar birkaç saniye geriden gelir (her sunucu 5 sn'de bir yazar) ve sayaç oluşturulduğu andan itibaren sayar; neyi saydığını değiştirmek onu sıfırdan başlatır. Bir verdict kendini saymaz.
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
- Dışa aktarma belgenin tamamını indirir: ayarlar ve izinli origin'ler, işlemler, öznitelikler, olay tipi etiketleri, doğrulamalar, liste tanımları, kendi parmak izleriniz, sinyal ağırlıkları ve sırasıyla kurallar. Gizli anahtarlar, API anahtarları, liste içerikleri veya trafik asla içinde yer almaz.
- İçe aktarma önce bir plan, sonra uygulamadır. Plan neyin oluşturulacağını, değişeceğini, silineceğini veya yeniden sıralanacağını gösterir ve içe aktarmanın tamamını kaydetmeden projenize karşı çalıştırır; böylece gerçek bir değişikliğin vereceği her hatayı gösterir (tanımlanmamış bir öznitelik kullanan koşul, olmayan bir doğrulamayı isteyen kural, …). Uygulama hepsini tek seferde kaydeder ya da hiçbirini kaydetmez; plandan sonra projeyi kimse değiştirmediyse.
- Belgeye koymadığınız bölümlere dokunulmaz; listelediğiniz bir öğe kayıtlı olanın
yerini alır; ayarlar mevcut ayarlarla birleşir. "Dosyada olmayanları sil"
varsayılan olarak kapalıdır. Kurallara bir
keyverin: kuralları projeler arasında eşleştirir (ve kuralın neden kodudur). - Uygulanıyor durumuna geçecek kurallar listelenir ve onayınızı ister. Yeni bir Turnstile / reCAPTCHA / hCaptcha / Friendly Captcha / GeeTest doğrulaması gizli anahtarını ister: planda girin ya da sonra Doğrulamalar sayfasından ayarlayın. Canlı bir projeye aktarırken yalnızca teste özel ayarlar (zorunlu karar) ve http / localhost origin'leri bir uyarıyla atlanır.
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.
- Neyi fark eder: denemelerde ani artış (önceki günlerin aynı saatiyle, genç bir proje için son iki saatle karşılaştırılır), birdenbire büyük pay alan bir ülke ya da ağ, başarısız doğrulamalarda sıçrama, engellenen ya da doğrulamaya yönlendirilen denemelerin payındaki değişim ve uyguladığınız bir kuralın eşiğine yaklaşan bir paylaşımlı sayaç. Duyarlılık (düşük · normal · yüksek) eşikleri ölçekler. Yeni bir bulgu uyarı gönderir; ikiye katlanan bir bulgu bir uyarı daha; süren bir bulgu 30 dakikada bir kısa bir güncelleme; bitişi ise bir "normale döndü" notu. Bir sayacı okuyan, Uygulanıyor durumundaki bir kural devreye girdiğinde (sayaç eşiğe ulaştığında ya da eşiği aştığında; örneğin tek bir saldırıda çok sayıda IP adresi) Nöbet değer başına değil, kural başına tek bir not gönderir: kuralı, ne yaptığını, sayacı, kaç IP adresi (kullanıcı, cihaz, …) için devrede olduğunu ve en fazla beş örneği söyler. Bu bir alarm değil, bilgi notudur: inceleme başlatılmaz, trafik beklenmedikse yapılacak bir şey yoktur. Güncellemeler yalnızca sayı arttığında ya da yeni bir değer göründüğünde gelir; 30 dakika sonra, sonra 1 saat, 2 saat, … (en fazla günde bir) ve proje başına saatte en fazla 6 not (sınırı aşanlar Nöbet sayfasında "gönderilmedi" olarak listelenir ve bir sonraki notta sayılır); kural devreden çıktığında bir not daha gelir.
- İnceleme, organizasyonunuzun asistanının LLM anahtarını kullanır (panel → Organizasyon ayarları). Trafiğinizi asistanla aynı araçlarla okur ve bir rapor yazar. Nöbet'i açan yöneticinin asistan çekmecesinde bir konuşmadır; Nöbet sayfasından açın. Asistan yoksa uyarıları yine alırsınız.
- Tek başına ne yapabilir: İzleniyor durumunda bir kural ekleyebilir (yalnızca ne yapacağını kaydeder; günde en fazla beş) ve yeni bir sayaç oluşturabilir. Başka hiçbir şey. İstediği diğer her değişiklik — bir kuralı uygulamak, düzenlemek ya da silmek, listeler, işlem ayarları, risk eşikleri — o yönetici asistanda onaylayana kadar raporda bir öneri olarak bekler; trafiğinizden okuduğu hiçbir şey hiçbir şeyi onaylayamaz. Nöbet hiçbir kararı kendi başına değiştirmez.
- Sınırlar: proje başına saatte en fazla 4, günde 24 inceleme (ikisi de ayarlanabilir), her biri en fazla 3 dakika; uyarılar sınırlanmaz. Nöbet'i kapatmak onu hemen durdurur.
- Bildirimler: seçtiğiniz ekip üyelerine e-posta (platform işletmecisinin ayarladığı
bir posta sunucusu gerekir) ve aşağıdaki
watch.alertilewatch.reportwebhook olayları (aşağıda). Sayılar, işlem adı, bir ülke kodu ya da ASN ve modelin özetini taşırlar. Bir sayacın değerleri için devrede olan bir kuralla ilgili not ayrıca bu değerlerden en fazla beşini örnek olarak anar; sayaç bunlara göre gruplanmışsa bunlar IP adresi, son kullanıcı kimliği ya da cihaz kimliği olabilir (Nöbet sayfası not başına en fazla 50 tanesini tutar); bir son kullanıcının e-posta adresi ya da başka verisi yoktur. - Verileriniz ve sağlayıcınız: bir inceleme sırasında asistanın araç sonuçları — trafiğinizden alıntılar: kullanıcı kimlikleri, IP adresleri, cihaz kimlikleri, öznitelikler — asistanla sohbet ettiğinizdeki gibi, sizin yapılandırdığınız LLM sağlayıcısına gider. Gizlilik notlarına bakın.
- Ayarlar kod olarak yapılandırmanın parçasıdır (bir ortama ait olan sahip, e-posta alıcıları ve dil hariç). Nöbet'in oluşturduğu kurallar Kurallar sayfasında Nöbet rozeti taşır.
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");
- Bilinen kullanıcı: dahili kimliğinizin, yalnızca sizin bildiğiniz bir anahtarla
(
ABUSEND_USER_ID_KEY, gizli anahtar gibi saklanır) HMAC'i (yukarıdaki gibi). - Giriş veya kayıt formu (yalnızca kullanıcının yazdığı): normalleştirilmiş giriş adının
HMAC'i (
login.trim().toLowerCase()) ya da hesabı bulup dahili kimliğini kullanın. - Aynı kişi, her çağrıda aynı kimlik —
DELETE /v1/users/{id}için de. Bir e-postanın anahtarsız, düz SHA-256'sı opak değildir: bir adres listesinden geri çözülebilir. - Uygulama doğrulamaları aynı kimlikle bildirilir (
challenges.result(id, status, { userId })).
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
- Eksik bir değerle yapılan her karşılaştırma yanlıştır —
!=dahil:amountyoksacontext.amount >= 100vecontext.amount != 0ikisi de yanlıştır. has(context.amount)varlığı test eder.!(context.amount >= 100)değer eksikken doğrudur; düzenleyici bir öznitelik karşılaştırmasını saran!için uyarır.has(context.amount) && context.amount < 100tercih edin.- Yanlış tipteki bir değer eksik sayılır (ve öznitelik üzerinde sayılır).
- Eksik veya okunamayan bir zaman damgasının
days_since(user.registered_at)sonucu eksiktir.
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. |
- Rastgele seçim, tek bir sağlayıcıya göre ayarlanmış bir çözücüyü daha az işe yarar hâle getirir ve ağırlıklar maliyeti dengeler; doğrulamaları aşılmaz yapmaz.
- Başarısızlık: başarısız, tamamlanmamış veya hata veren bir adım o doğrulamanın
on_fail/on_incomplete/on_errorayarını uygular. "Bunlardan biri" bir başarısızlıktan sonra asla başka bir doğrulamaya geçmez ve tekrar denemek asla yeniden seçim yapmaz: bekleyen adım, sonucu gelene kadar geri döner (reissued: true). Yeni bir deneme (yeni bir akış) yeniden seçer. "Hepsi aynı anda": bazı doğrulamalar geçilmediyse onların sonuçlarının en katısı karar verir (Engelle, Karar uygulamanızda'dan önce gelir); ikiden birini geçmek asla yetmez. - Sınırlar: her doğrulama adımı akış başına projenin bütçesine sayılır (varsayılan 3,
Proje ayarları → Deneme başına doğrulama ile en fazla 5; üç doğrulamalı paralel bir grup üç
kullanır) ve bütçe eşleşen tüm kurallarca paylaşılır: eşleşen kuralların istediği
doğrulamalar sığmıyorsa akış, kimse doğrulama yapmadan review
challenge_limitile biter (decided_by.namesığmayan kuraldır), her doğrulama akışta en fazla bir kez; gönderim sınırı her uygulama doğrulaması için ayrıdır — paralel bir gruptaki bir uygulama doğrulaması sınıra ulaştıysa hiçbir adım verilmez ve o doğrulamanınon_incompleteayarı uygulanır. passed("sms")ve hatırlanan başarılar doğrulama başına çalışır; hatırlanan bir başarı grubun kendi öğesini karşılar.- Kancalarınız ve handler'larınız yalnızca verilen doğrulamaları görür:
checks.<key>(Node) her yeni adım için bir kez çalışır,Abusend.fetchbir 428'deki her adımı çalıştırır. - Panel API ve MCP:
"checks": {"mode": "any", "items": [{"check": "turnstile", "weight": 70}, {"check": "hcaptcha", "weight": 30}]}, tek doğrulama için"check": "sms". Ağırlıklar (0–100 arası tam sayı, varsayılan 1, en az biri 0'dan büyük) yalnızcaanyiçin vardır.
Akışlar, yeniden denemeler ve challenge kimlikleri
- Bir doğrulama adımı projesine, işlemine ve kullanıcısına, geçerli bir token ile
verildiyse cihaza da bağlıdır. Eşleşmeyen bir kimlik (
challenge_ids) — bilinmeyen, başka işlem veya kullanıcı, başka cihaz ya da başka bir sekmenin devraldığı — kredi vermez (bilgi nedenichallenge_invalid); asla HTTP hatası değildir. - Gönderilen kimlikler tek bir akış için sayılır (gönderilenler arasında en son verilen adımın akışı); başka bir akışın kimlikleri kredi vermez.
- Bir kimliği saklamak asla işe yaramaz. Kullanılabilir bir kimlik yoksa, aynı işlem ve kullanıcının (kullanıcı yoksa cihazın) en uzun doğrulama süresi + 15 dakika içinde verilmiş, tüketilmemiş ve geçilmemiş son adımı akışını ekler. Geçilmiş bir adım bu yolla asla eklenmez: kredi yalnızca gönderilen ve eşleşen bir kimlikten gelir.
- Akışın bir adımı beklerken bekleyen her adım
reissued: trueile yeniden döner (sayılmaz; kancalar yeniden çalışmaz) — kimliği gönderilmiş olsun ya da olmasın. Her birinin sonucu gelince verdict akışın sonuçlarını kullanır ve akışın tüm adımlarını (göndermediğiniz biri dahil) tüketir: bir başarılar kümesi tek bir karar kazandırır. - Tüketilmiş bir adım 5 sn içinde aynı çağıran (kullanıcı,
client_ip, cihaz, işlem) tarafından yeniden gönderilirse saklanan yanıt tekrarlanır (idempotent tekrarlar); diğer her durum denychallenge_reused(retry_after_seconds: 5). - Bir başarı 5 dakika içinde kullanılmalıdır.
- Akış başına en fazla projenin bütçesi kadar adım (varsayılan 3, en fazla 5; birlikte
verilenler dahil), her doğrulama bir kez; eşleşen kurallar daha fazlasını isterse → review
challenge_limit, kimse doğrulama yapmadan önce.
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:
- Widget: başarıdakiyle aynı cihaz kimliği, parmak izi özeti ve IPv4 /24 · IPv6 /48; 24 saatte en fazla 2 kullanıcısı olan bir cihazda.
- Uygulama: aynı kullanıcı (ikisinde de cihaz varsa aynı cihaz).
remember_secondsiçinde başarı başına en fazla 20 atlama. Kural listesi "son 24 saatte SMS geçildiyse atlanır" gösterir (bir grup için: "bunlardan biri" — doğrulamalarının en uzun süresi; diğer modlar — en kısası).- Her zaman tarayıcının kendi cihaz kimliği: tanımanın tarayıcıyı bağladığı cihaz başarılarını asla ödünç vermez.
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.
- 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
allowdöner, İzleniyor durumundaki kuralların ne yapacağıwould_decision/would_challengealanlarındadır. Yerleşik deny'lar (kullanılamayan token'lar, engel listeleri) ve token zorunluluğu her zaman uygulanır. - 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_attributeslisteleyen bir kural, sunucunuzun hiç göndermediği bir özniteliği okuyordur: önce entegrasyonu düzeltin. - "Karar uygulamanızda" durumunu bilinçli olarak ele alın (
onReview); Genel bakış, verdict'lerin çoğu review ile biten işlemleri işaretler. - 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.
- 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_…):
http://localhost:*,127.0.0.1ve[::1]her zaman izinli origin'lerdir; boş liste her origin'e izin verir. Verdict'ler"test": truetaşır.- Zorunlu karar (Kurulum → Test) veya istek başına
simulate: "deny"verdict'i o karar yapar (decided_by.type = "test", nedentest_forced) — bir doğrulama adımı verilirken hariç. Live projeler ikisini de yok sayar. - Sağlayıcı test anahtarları (yalnızca test projeleri, asla live):
Turnstile site anahtarı
1x00000000000000000000AA(geçer) /2x00000000000000000000AB(tarayıcıda başarısız olur), gizli anahtar1x0000000000000000000000000000000AA(geçer) /2x0000000000000000000000000000000AA(başarısız); reCAPTCHA v2 site anahtarı6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhIve gizli anahtar6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe(geçer); hCaptcha site anahtarı10000000-ffff-ffff-ffff-000000000001ve gizli anahtar0x0000000000000000000000000000000000000000(geçer). Friendly Captcha ve GeeTest test anahtarı yayımlamaz:simulate, zorunlu karar ya dalocalhostüzerinde gerçek bir anahtar kullanın. abusend'in kendi doğrulamaları hiç anahtar istemez:localhostüzerinde de canlıdaki gibi çalışır. - Uygulama doğrulamaları: test OTP uç noktanızdan
passed/failed/abandonedbildirin. - Headless test tarayıcıları riski bilerek yükseltir; CI'da
simulate, zorunlu karar veya CI çıkış IP'nizi izinli IP'ler listesinde kullanın.
Cihaz kimliği
- Proje kapatmadıkça (
privacy.device_id = off),/v1/assessimzalı, proje başına bir kimlik (d1.…) verir; edge alan adında bölümlenmiş (partitioned), HttpOnly bir__Host-ab_didçerezinde ve sitenizinlocalStorage["abusend.did"]alanında tutulur. Sahte veya yabancı bir kimlik yok sayılır ve yenisiyle değiştirilir. device.*alanlarını, hatırlanan widget başarılarını, doğrulama adımı bağını (geçerli bir token ile verilmiş bir adım yalnızca aynı cihazdan sayılır) ve Cihazlar sayfasını besler. Bir cihazı engellemek, onu taşıyan her verdict'i reddeder (blocklist_device). Bir cihaza izin vermek en üstteki allow kuralıallowlist_deviceolur ve yalnızca izin verildiği tarayıcıdan sayılır (aynı parmak izi özetiyle çerez kimliği).- Çevrim içi bir tanımlayıcıdır — KVKK/GDPR kapsamında kişisel veridir. Çerezi aydınlatma metninizde belirtin (PRIVACY.md); kapalıyken hiçbir şey verilmez, saklanmaz veya ayarlanmaz.
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.
- Yalnızca masaüstü tarayıcılar (Windows, macOS, Linux, ChromeOS). iPhone, iPad (her tarayıcı), Android ve diğer telefon ve tabletler asla tanınmaz: aynı modeller aynı görünür.
- Yalnızca aynı ağ. Edge, tarayıcının kararlı parmak izinin bir özetini (canvas, WebGL,
ses ve yazı tipi özetleri, ekran, saat dilimi, diller, işletim sistemi, işlemci
çekirdekleri, TLS parmak izi, tarayıcı ve ana sürümü) projenin son 30 günde aynı IPv4 /24 ·
IPv6 /48 bloğunda gördüğü cihazlarla karşılaştırır.
high: aynı özete sahip tam olarak bir cihaz.medium: aynı tarayıcı ailesinden, bileşenlerinin çoğu aynı olan ve eşit derecede iyi başka bir cihazın bulunmadığı tek cihaz. Daha zayıf adaylar asla bağlanmaz. Gönderilen bir kimlik her zaman önceliklidir. - Yoğun ağlar atlanır: son 7 günde özeti olan 20'den fazla cihazın görüldüğü bir /24 · /48 (mobil operatör NAT'ı, ofisler, kampüsler, oteller) asla kullanılmaz.
- Paylaşılan parmak izleri bırakılır: aynı özete sahip iki cihaz kendi kimlikleriyle gelmeye devam ettiğinde (aynı makineler, bir bilgisayarda iki tarayıcı profili) özet paylaşılan olarak işaretlenir ve bir daha asla bağlanmaz. Aynı ağdaki aynı makineler, ikincisinin kimliği bir günlük olana kadar bir kez bağlanabilir.
- Kurallar
device.recognized,device.recognition,device.recognized_blocked(bağlanan cihazblock_deviceslistesinde ya dablock_userslistesindeki bir kullanıcı tarafından kullanıldı) vedevice.recognized_users(bağlanan cihazın 30 gündeki farklı kullanıcıları) alanlarını görür. Bağlantı kurallarınız için bir ipucudur — asla otomatik engel değildir, güven asla taşınmaz: engel ve izin listeleri, hatırlanan başarılar,device.age_seconds,device.users_totalveuser.new_deviceyeni kimliğe aittir. Bağlantının ne anlama geldiğine siz karar verin, ör.device.recognized && device.recognized_blocked→ ‹check› ile doğrula. - Bir ipucudur, kanıt değildir. Safari gizli modu, Firefox ve Brave parmak izlerini
rastgeleleştirir: tanınmazlar ve
fingerprint_randomizedsinyalini üretirler. Özellikledevice.recognition == "medium"için Engelle yerine doğrulamayı tercih edin. - Yalnızca anahtarlı özetler saklanır; 30 gün sonra, silinen bir kullanıcının cihazlarıyla birlikte ve tanımayı kapattığınızda hepsi birden silinir. Bu bir cihaz parmak izi kullanımıdır: önce aydınlatma metninizi güncelleyin (PRIVACY.md).
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
- Bir token (
abt_…) opak ve şifrelidir, varsayılan olarak 10 dakika geçerlidir (proje başına 60–3600 sn) ve tek kullanımlıktır. SDK token'ı gönderim anında ister; önceden almayın. - Aynı token için tekrarlanan bir verdict, yalnızca ilk kullanımdan sonraki 5 sn
içinde, en fazla 3 kullanımla, aynı
action,client_ipve kullanıcıyla yapılırsa idempotent bir tekrardır. Diğer her durumtoken_reused→ deny. Yalnızca ağ hatalarında ve 5xx'te, birebir aynı gövdeyle tekrar deneyin; her kullanıcı işlemi için yeni bir token alın. - Süresi dolmuş, geçersiz ve başka projeye ait token'lar her zaman reddedilir — test
projesinden bir token'ı live gizli anahtarla kontrol etmek
token_project_mismatcholarak görünür. Servisin kaydını artık tutmadığı bir token (arada bir servis yeniden başlatması; en fazla token'ın ömrü kadar)token_invalidolur, asla geçerli olmaz: yeni bir token alın.
Hata politikası ve gecikme
- Verdict çağrısı 1,5 sn sonra zaman aşımına uğramalıdır. Zaman aşımında, ağ hatasında veya 5xx'te hata politikanızı uygulayın — SDK'ların varsayılanı review'dur.
- 503
busybir kesintidir. Ani yükte servis, bir an için daha fazla verdict alamadığındaPOST /v1/verdictçağrısını503busyveRetry-After: 1ile yanıtlayabilir. Hata politikanızı uygulayın (review veya deny, asla allow değil); başlıktaki süre sonra aynı gövdeyle yeniden denemek uygundur.@abusend/nodebunu zaten yapar (penceresi içinde yeniden dener, sonradecided_by.type = "outage"ile hata kararını döndürür). 503state_unavailableaynı türden bir kesintidir: servisin durum deposuna ulaşılamadı, bu yüzden token kullanılamadı (ya da/v1/assess'te token verilmedi); hiçbir şey tüketilmedi, aynı gövdeyle yeniden denemek uygundur. - 429 bir kesinti değildir. Review'a (veya deny'a) çevirin, asla allow'a değil: uç noktanızı doldurabilen herkes anahtarınızı limitin üstüne itebilir.
- Diğer 4xx hataları entegrasyon hatalarıdır (yanlış anahtar, geçersiz girdi): bunlar için alarm kurun.
- Hedef sunucu süresi: verdict p99 < 50 ms. Doğrulama istenen bir istek bir ek gidiş-dönüş (428) maliyetindedir; doğrulama istenmeyen istekler etkilenmez.
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ı
- Kimlik değil, opak kullanıcı kimlikleri ve gerçekler gönderin (kullanıcı kimliği seçimi).
- abusend her token isteği için sinyalleri, her verdict için girdilerini (kullanılan
kullanıcı öznitelikleri, bağlam, sayaçlar) ve akışın doğrulama adımlarını saklar.
Saklama süresi proje başınadır (varsayılan 30 gün);
DELETE /v1/users/{id}bir kullanıcıyı siler, verdict girdilerini temizler ve doğrulama adımlarıyla bağını koparır. - Bildirdiğiniz olaylar tipi, değeri, zamanı, kimliği ve özellikleriyle projenin olay saklama süresi boyunca (varsayılan 90 gün) saklanır ve kullanıcıyla birlikte silinir. Olay kimliklerine ve özelliklerine kişisel veri koymayın.
- Widget doğrulamaları: tarayıcı sağlayıcının betiğini yükler (Cloudflare, Google, hCaptcha/Intuition Machines, Friendly Captcha (Almanya) veya GeeTest (Çin)) ve edge sonucu onunla doğrular — sağlayıcı ziyaretçinin IP adresini ve tarayıcı verilerini alır. GeeTest verileri Çin'de işler ve saklar: bağlamadan önce yurt dışına aktarım kurallarını (KVKK, GDPR) kontrol edin. abusend'in kendi doğrulamaları başka kimseye bir şey göndermez: hold / trace sırasındaki işaretçi hareketleri edge'inizde puanlanır ve saklanmaz. Uygulama doğrulamalarını (SMS, e-posta, KYC) siz kendi veri işleyenlerinizle yürütürsünüz; abusend yalnızca sonucu kaydeder.
- Nöbet denemeleri işlem başına 5 dakikalık kovalarda sayar (toplamda ve ülke ile ASN başına: toplamlardır, kişisel veri yoktur; 8 gün saklanır). İncelemelerini sizin asistanınız yürütür: modelin okuduğu araç sonuçları — trafiğinizden alıntılar — yapılandırdığınız LLM sağlayıcısına gider. Uyarıları ve raporları sayı taşır; bir sayacın kuralıyla ilgili not, sayacın gruplandığı değerlerden (IP adresi, kullanıcı ya da cihaz kimliği) en fazla beşini anar, e-posta adresi asla.
- Ayrıntılar, alt işleyenler ve KVKK eki: PRIVACY.md.
Güvenlik kontrol listesi
- Gizli anahtar yalnızca sunucunuzda durur — asla tarayıcıda, mobil uygulamada veya depoda değil.
- Kararlar sunucu tarafında
/v1/verdict(veyaprotect()/guard()) ile verilir, asla bir istemci bayrağıyla değil. - Her korunan istek kendi token'ını kullanır;
actionve gerçekclient_ipgönderilir. -
user.idopaktır (HMAC), asla e-posta veya telefon numarası değildir; öznitelik ve bağlamda kişisel veri yoktur. -
onReviewher route için bilinçli bir seçimdir; bir doğrulama adımının veya limitin verdiği review'lara devam edilmez. - Uygulama sonuçları:
failedyalnızca kendi deneme sınırınızdan sonra;userIdher zaman gönderilir; web akışlarında challenge kimliği oturumda tutulur. - Kesintiler ve 429 review'a (veya deny'a) çevrilir, asla allow'a değil.
- Live projeler: izinli origin'ler tam olarak ayarlı; üretim
pk_live_/sk_live_kullanır vetest === falsekontrol eder. - CSP, edge'e ve widget doğrulamalarınızın sağlayıcılarına izin verir.
- Kurallar uygulanmadan önce İzleniyor durumundan ve yeniden oynatmadan geçer; hiçbir kural
unseen_attributeslistelemez. - Panel hesapları iki adımlı doğrulama kullanır; silme talepleri
DELETE /v1/users/{id}çağırır.
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.