KaizoCoreKaizoCore docs

The /v1/decide API

Full request/response schema, authentication, and error handling.

Endpoint

POST https://api.kaizocore.com/v1/decide

Authentication

Every request must be HMAC-signed. Four headers are required:

HeaderMeaning
X-KZ-KeyYour pk_live_... or sk_live_... key
X-KZ-TsUnix timestamp (seconds) — requests outside a ±5 minute window are rejected
X-KZ-RidA unique request ID — reused IDs are rejected as replays
X-KZ-SigHMAC-SHA256 signature, hex-encoded

Signing string (newline-joined, in this exact order):

METHOD
PATH
TIMESTAMP
SHA256(body)
REQUEST_ID

HMAC key: if you're signing with sk_live_... (your backend secret key), the key is the secret key itself. If you're signing with pk_live_... (safe to embed client-side — this is what the collector script uses), the key is SHA256(your public key) — this lets the SDK sign requests without ever needing your server secret, while still getting body integrity and replay protection.

# Example: signing a request with sk_live_
BODY='{"ip":"203.0.113.7","session_token":"st_abc123","event_type":"checkout"}'
TS=$(date +%s)
RID=$(uuidgen)
BODY_HASH=$(echo -n "$BODY" | shasum -a 256 | awk '{print $1}')
SIGNING_STRING="POST
/v1/decide
$TS
$BODY_HASH
$RID"
SIG=$(echo -n "$SIGNING_STRING" | openssl dgst -sha256 -mac HMAC -macopt hexkey:$(echo -n "$SK_LIVE_KEY" | xxd -p -c 256) | awk '{print $2}')

curl -X POST https://api.kaizocore.com/v1/decide \
  -H "Content-Type: application/json" \
  -H "X-KZ-Key: $SK_LIVE_KEY" \
  -H "X-KZ-Ts: $TS" \
  -H "X-KZ-Rid: $RID" \
  -H "X-KZ-Sig: $SIG" \
  -d "$BODY"

Request body

{
  "ip": "203.0.113.7",
  "user_agent": "Mozilla/5.0 ...",
  "url": "https://yourapp.com/checkout",
  "referrer": "https://yourapp.com/cart",
  "session_token": "st_abc123...",
  "entity_keys": { "user_id": "42", "email": "user@example.com" },
  "headers": { "x-kz-ja3": "..." },
  "event_type": "checkout",
  "timestamp": 1752480000
}
FieldTypeRequiredNotes
ipstringRecommendedUsed for IP enrichment, velocity, and impossible-travel checks. Omitting it doesn't error, but disables everything IP-derived.
user_agentstringRecommendedFeeds device-layer rules (known-bot UA fragments, etc).
urlstringOptionalCurrent page URL — used for journey confidence.
referrerstringOptionalUsed for direct-hit detection.
session_tokenstringEffectively requiredThe token the collector SDK generated. Missing it returns SOFT_CHALLENGE immediately — see below.
entity_keysobjectOptionalAny identity you want tracked for velocity/impossible-travel — user_id, email, anything meaningful to your flow.
headersobjectOptionalPass through raw headers like x-kz-ja3 (TLS fingerprint) if your infra has them available.
event_typestringRecommendedlogin, checkout, signup, payment, search — used for per-action thresholds.
timestampint64OptionalClient-reported timestamp, informational.

Missing session_token

If session_token is empty, KaizoCore returns SOFT_CHALLENGE immediately without running the full scoring engine — this is a customer-integration signal (the SDK isn't wired up correctly, or a legitimate non-JS client is calling in), and the deliberate choice is to challenge, never hard-block, since blocking here would drop real users behind a broken integration.

Response body

{
  "request_id": "a6af22a9-3a6c-42e8-b28f-3e3184093d79",
  "decision": "SOFT_CHALLENGE",
  "score": 32.5,
  "reasons": ["datacenter_ip", "missing_accept_headers"],
  "review_hint": "",
  "latency_ms": 187,
  "session_mode": "FORGE:BOUND",
  "shadow_routed": false
}
FieldTypeNotes
request_idstringUnique per call — use this to correlate with webhook deliveries and dashboard lookups.
decisionstringOne of ALLOW, ALLOW_WATCH, SOFT_CHALLENGE, REVIEW, BLOCK — see Decision scores.
scorenumberThe fused 0–100 risk score.
reasonsstring[]Every rule that fired, de-duplicated. Always an array, never null.
review_hintstringOptional guidance for manual review queues.
latency_msintServer-side processing time.
session_modestringSee Session modes.
shadow_routedbooltrue if this session was additionally routed to the shadow review queue (MIRROR/GHOST).

Rate limiting

Every response includes rate-limit headers:

X-KZ-Rl-Limit: 2000
X-KZ-Rl-Remaining: 1847
X-KZ-Rl-Reset: 1752480060

A 429 includes Retry-After in seconds.

Error responses

StatusMeaning
400Malformed JSON body
401Signature verification failed, or unknown API key
413Body exceeds the 64 KB limit
429Rate limit exceeded
500Internal error — genuinely rare; the engine is built to degrade gracefully (e.g. ML models absent, IP enrichment failed) rather than error out

What to do with the decision

DecisionSuggested handling
ALLOWProceed normally.
ALLOW_WATCHProceed normally — this band is informational, for your own analytics.
SOFT_CHALLENGEShow a lightweight challenge (CAPTCHA, email OTP) before proceeding.
REVIEWProceed, but flag for manual/async review — useful for e.g. holding an order for fraud review without blocking checkout outright.
BLOCKDeny the action.

These are suggestions, not requirements — you're free to map decisions to your own UX. Per-action thresholds let you tune the score boundaries themselves per event type.

On this page