requireMerchantAuth reads Authorization and accepts only Bearer plus a token that starts with mk_live_. A missing header, a non-Bearer scheme, any other prefix, an unknown key, a revoked key, or a completed account deletion is 401 with error Unauthorized and code UNAUTHORIZED. An expired key is 401 with error API key expired and the same code.
The key must be allowed to access {storeId}: the key owner can access that shop, and a key limited to selected shops must list it. The key owner must have manageBlacklist on that shop. A restricted key must include the scope named on the method. antifraud:write also satisfies antifraud:read. A full-access key skips the scope list and still needs manageBlacklist. A failed shop, scope, or permission check is 403 with error Forbidden and code FORBIDDEN.
storeId in the JSON body is ignored. The shop is the {storeId} in the path.
Merchant routes share one limit of 120 requests per 60 seconds per client IP. Over the limit the response is 429 with error Too many requests and code RATE_LIMITED.
Get settings
antifraud:read
Status 200. A shop the key cannot access, including a shop that does not exist, is 403 Forbidden from requireMerchantAuth. 404 with error Store not found and code NOT_FOUND is returned only when the store row is missing after those access checks pass.
These are the only fields returned. A missing or non-object stored config comes back as the defaults above (
blockVpnProxy false, emailValidation disabled, empty domains, autoBlockOnChargeback false, kickBlockedFromDiscord false, discordGuildId null). A stored object still returns all six fields, with those defaults for missing or invalid values.
Update settings
antifraud:write
PUT runs the same handler as PATCH. Status 200. The response is the saved settings object, same fields as GET. A store row missing after the access checks is 404 Store not found, same as GET.
Send one or more of the fields below. Unknown keys are dropped. A body with no recognized fields returns 400 with error No updatable anti-fraud fields provided and code BAD_REQUEST. Invalid JSON and JSON body must be an object use the same code. Field errors are the Zod issues joined with ; .
Omitted fields stay as they are.
blockVpnProxy: true when the plan has no VPN and TOR detection returns 403 with error VPN & TOR detection requires Premium or Business. Upgrade in Billing., code plan_vpn_tor, requiredPlan premium, and upgradePath /billing. Setting it to false, or omitting it, does not hit that check. No other anti-fraud field is plan-gated.
Audit log
antifraud:read
Query:
There is no
page parameter. A page query is ignored. Rows are newest createdAt first. This route does not look up the store row: a shop the key cannot access is 403, and an accessible shop with no matching rows returns { "logs": [] }.
Categories this product records: blacklist_rule, vpn_proxy, customer_blocked, ip_velocity, email_velocity, email_domain, chargeback, settings. The filter matches the stored category string.
subject, subjectType, and detail are strings or null. createdAt is an ISO timestamp. metadata is not returned.
Invalid query values return 400.
Errors
Error JSON is
{ "success": false, "error": "...", "code": "..." }. The VPN plan gate also returns requiredPlan and upgradePath. A 500 also returns errorId.

