Cloudflare Workers
Cloudflare Edge Workers

Install

npm / Wrangler

Install the package:

1
npm i @botbye/cloudflare-worker

Requires @cloudflare/workers-types >= 4 as a peer dependency.

Dashboard

Paste the pre-built bundle directly into your worker's index.js. No build step required — the SDK is available as BotBye.init, BotBye.evaluate, etc.:

1
var BotBye=(()=>{var _r=Object.create;var D=Object.defineProperty;var Or=Object.getOwnPropertyDescriptor;var Rr=Object.getOwnPropertyNames;var Nr=Object.getPrototypeOf,Tr=Object.prototype.hasOwnProperty;var o=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports),gr=(e,r)=>{for(var t in r)D(e,t,{get:r[t],enumerable:!0})},ge=(e,r,t,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of Rr(r))!Tr.call(e,a)&&a!==t&&D(e,a,{get:()=>r[a],enumerable:!(i=Or(r,a))||i.enumerable});return e};var pe=(e,r,t)=>(t=e!=null?_r(Nr(e)):{},ge(r||!e||!e.__esModule?D(t,"default",{value:e,enumerable:!0}):t,e)),pr=e=>ge(D({},"__esModule",{value:!0}),e);var v=o(l=>{"use strict";Object.defineProperty(l,"__esModule",{value:!0});l.once=l.getIpFromHeaders=l.extractMessageFromError=l.isObject=l.isNumber=l.withTimeout=l.isKeyExist=l.toString=l.isNotNil=l.isPlainObject=l.isString=void 0;var ye=function(e,r){return typeof e===r},Lr=function(e){return e==null},yr=function(e){return!Lr(e)};l.isNotNil=yr;var se=function(e){return ye(e,"string")};l.isString=se;var hr=function(e){return ye(e,"number")&&!Number.isNaN(e)};l.isNumber=hr;var le=function(e){return!(typeof e!="object"||e===null)};l.isObject=le;var Ir=function(e){if(typeof e!="object"||e===null)return!1;var r=Object.getPrototypeOf(e);return r===Object.prototype||r===null};l.isPlainObject=Ir;var Sr=function(e){return se(e)?e:String(e)};l.toString=Sr;var Ar=function(e,r){return le(e)&&r in e};l.isKeyExist=Ar;var Cr=function(e,r,t){var i=new Promise(function(a,n){return setTimeout(function(){n(t)},r)});return Promise.race([e,i])};l.withTimeout=Cr;var mr=function(e){var r="Unknown error";return e instanceof Error&&(r=e.message),r};l.extractMessageFromError=mr;var Le=function(e){var r=Array.isArray(e)?e[0]:e;if(!se(r))return null;var t=r.split(",")[0].trim();return t||null},Ur=function(e){var r;return le(e)?(r=Le(e["x-forwarded-for"]))!==null&&r!==void 0?r:Le(e["x-real-ip"]):null};l.getIpFromHeaders=Ur;var qr=function(e){var r=!1;return(function(){for(var t=[],i=0;i<arguments.length;i++)t[i]=arguments[i];r||(r=!0,e.apply(void 0,t))})};l.once=qr});var ve=o(k=>{"use strict";Object.defineProperty(k,"__esModule",{value:!0});k.getModuleHeaders=void 0;var Fr=function(e){var r;return r={},r["Module-Name"]=e.module.name,r["Module-Version"]=e.module.version,r};k.getModuleHeaders=Fr});var Ie=o(H=>{"use strict";Object.defineProperty(H,"__esModule",{value:!0});H.sendInitCall=void 0;var br=v(),Vr=ve(),he="Invalid JSON in the server response",Pr=function(e){var r=e.global,t=r.httpClient.call(r.url+"/init-request/v1",{method:"POST",headers:(0,Vr.getModuleHeaders)(r),body:{serverKey:r.serverKey}}),i=t.result,a=t.abort,n=i.then(function(u){var s={error:"Unknown error"};try{s=JSON.parse(u)}catch{throw new Error(he)}if(!(0,br.isPlainObject)(s))throw new Error(he);if("status"in s&&s.status==="ok")return"";if("error"in s)throw new Error(s.error);return""});return{result:n,abort:a}};H.sendInitCall=Pr});var Se=o(L=>{"use strict";var B=L&&L.__assign||function(){return B=Object.assign||function(e){for(var r,t=1,i=arguments.length;t<i;t++){r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},B.apply(this,arguments)};Object.defineProperty(L,"__esModule",{value:!0});L.sendEvaluateCall=void 0;var Mr=ve(),jr="/api/v1/protect/evaluate",wr=function(e){var r,t=e.global,i=e.event,a="".concat(t.url).concat(jr);try{var n=new URL("".concat(a,"?").concat((r=i.request.token)!==null&&r!==void 0?r:""));a=n.href}catch{}return t.httpClient.call(a,{method:"POST",headers:(0,Mr.getModuleHeaders)(t),body:B(B({},i),{server_key:t.serverKey})})};L.sendEvaluateCall=wr});var ce=o(y=>{"use strict";Object.defineProperty(y,"__esModule",{value:!0});y.EVENT_INFO_EVENT_STATUSES=y.EVENT_VALIDATION_TYPE=void 0;var Kr=["SUCCESSFUL","FAILED","ATTEMPTED","UNKNOWN"];y.EVENT_INFO_EVENT_STATUSES=Kr;var Dr=["validate","risk","full"];y.EVENT_VALIDATION_TYPE=Dr});var h=o(E=>{"use strict";Object.defineProperty(E,"__esModule",{value:!0});E.fieldRequiredLog=E.sanitizeIfPlainObject=E.sanitizeIfTruthy=E.sanitizeIfNotNil=E.sanitize=void 0;var Ee=v(),Ae=function(e,r,t,i,a,n,u){if((0,Ee.isKeyExist)(e,t)){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitize=Ae;var kr=function(e,r,t,i,a,n,u){if((0,Ee.isNotNil)(e[t])){Ae(e,r,t,i,a,n,u);return}u&&(r[i]=u)};E.sanitizeIfNotNil=kr;var Hr=function(e,r,t,i,a,n,u){if(e[t]){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitizeIfTruthy=Hr;var Br=function(e,r,t,i,a,n,u){if((0,Ee.isPlainObject)(e[t])){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitizeIfPlainObject=Br;var Wr=function(e,r,t,i){e[t]||r.warn("Field ".concat(t," is required in ").concat(i))};E.fieldRequiredLog=Wr});var qe=o(R=>{"use strict";Object.defineProperty(R,"__esModule",{value:!0});R.adaptRequestInfoWithOptionalHeaders=R.adaptRequestInfoWithRequiredHeaders=R.FALLBACK_EVENT_REQUEST_INFO=void 0;var g=v(),T=h(),Ce="0.0.0.0",me={},zr={ip:Ce,headers:me};R.FALLBACK_EVENT_REQUEST_INFO=zr;var xr=function(e){if(!(0,g.isPlainObject)(e))return me;for(var r={},t=0,i=Object.keys(e);t<i.length;t++){var a=i[t];r[a]=(0,g.toString)(e[a])}return r},Ue=function(e){return function(r,t){var i=(0,g.isPlainObject)(r)?r:{},a={};return(0,T.fieldRequiredLog)(i,t.logger,"ip","evaluateEvent.request"),e&&(0,T.fieldRequiredLog)(i,t.logger,"headers","evaluateEvent.request"),(0,T.sanitizeIfTruthy)(i,a,"ip","ip",g.toString,t,Ce),(0,T.sanitizeIfNotNil)(i,a,"token","token",g.toString,t),(0,T.sanitizeIfPlainObject)(i,a,"headers","headers",xr,t,{}),(0,T.sanitizeIfNotNil)(i,a,"requestMethod","request_method",g.toString,t),(0,T.sanitizeIfNotNil)(i,a,"requestUri","request_uri",g.toString,t),a}},Gr=Ue(!0);R.adaptRequestInfoWithRequiredHeaders=Gr;var Qr=Ue(!1);R.adaptRequestInfoWithOptionalHeaders=Qr});var Ve=o(I=>{"use strict";Object.defineProperty(I,"__esModule",{value:!0});I.adaptEventInfo=I.FALLBACK_EVENT_EVENT_INFO=void 0;var de=v(),Fe=ce(),W=h(),fe="UNKNOWN",be="UNKNOWN",Jr={type:be,status:fe};I.FALLBACK_EVENT_EVENT_INFO=Jr;var Yr=function(e,r){var t=(0,de.toString)(e).trim().toUpperCase();return Fe.EVENT_INFO_EVENT_STATUSES.includes(t)?t:(r.logger.warn("Event status should be one of: ".concat(Fe.EVENT_INFO_EVENT_STATUSES.join(" || "))),fe)},Xr=function(e,r){var t=(0,de.isPlainObject)(e)?e:{},i={};return(0,W.fieldRequiredLog)(t,r.logger,"type","evaluateEvent.event"),(0,W.fieldRequiredLog)(t,r.logger,"status","evaluateEvent.event"),(0,W.sanitize)(t,i,"type","type",de.toString,r,be),(0,W.sanitizeIfTruthy)(t,i,"status","status",Yr,r,fe),i};I.adaptEventInfo=Xr});var Me=o(S=>{"use strict";Object.defineProperty(S,"__esModule",{value:!0});S.adaptUserInfo=S.FALLBACK_EVENT_USER_INFO=void 0;var F=v(),b=h(),Pe="00000000-0000-0000-0000-000000000000",Zr={account_id:Pe};S.FALLBACK_EVENT_USER_INFO=Zr;var $r=function(e,r){var t=(0,F.isPlainObject)(e)?e:{},i={};return(0,b.fieldRequiredLog)(t,r.logger,"accountId","evaluateEvent.user"),(0,b.sanitizeIfTruthy)(t,i,"accountId","account_id",F.toString,r,Pe),(0,b.sanitizeIfNotNil)(t,i,"username","username",F.toString,r),(0,b.sanitizeIfNotNil)(t,i,"email","email",F.toString,r),(0,b.sanitizeIfNotNil)(t,i,"phone","phone",F.toString,r),i};S.adaptUserInfo=$r});var we=o(A=>{"use strict";Object.defineProperty(A,"__esModule",{value:!0});A.adaptEvaluateConfig=A.FALLBACK_EVENT_EVALUATE_CONFIG=void 0;var et=v(),rt=h(),je={};A.FALLBACK_EVENT_EVALUATE_CONFIG=je;var tt=function(e){return!!e},it=function(e,r){if(!(0,et.isPlainObject)(e))return je;var t={};return(0,rt.sanitizeIfNotNil)(e,t,"bypassBotValidation","bypass_bot_validation",tt,r),t};A.adaptEvaluateConfig=it});var De=o(z=>{"use strict";Object.defineProperty(z,"__esModule",{value:!0});z.adaptCustomFields=void 0;var Ke=v(),at={},nt=function(e,r){if(!(0,Ke.isPlainObject)(e))return at;for(var t={},i=0,a=Object.keys(e);i<a.length;i++){var n=a[i];t[n]=(0,Ke.toString)(e[n])}return t};z.adaptCustomFields=nt});var ze=o(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});G.adaptEvent=void 0;var _e=v(),ke=ce(),c=h(),V=qe(),He=Ve(),Be=Me(),x=we(),We=De(),ut=function(e,r){var t=(0,_e.isPlainObject)(e)?e:{},i={},a=String(t.type).trim().toLowerCase();if(ke.EVENT_VALIDATION_TYPE.includes(a)||(r.logger.warn("Event type should be one of: ".concat(ke.EVENT_VALIDATION_TYPE.join(" || "))),a="validate"),i.type=a,a==="validate"&&((0,c.fieldRequiredLog)(t,r.logger,"request","validate event"),(0,c.sanitize)(t,i,"request","request",V.adaptRequestInfoWithRequiredHeaders,r,V.FALLBACK_EVENT_REQUEST_INFO),(0,c.sanitizeIfNotNil)(t,i,"config","config",x.adaptEvaluateConfig,r),(0,c.sanitizeIfNotNil)(t,i,"customFields","custom_fields",We.adaptCustomFields,r)),a==="full"||a==="risk"){(0,c.fieldRequiredLog)(t,r.logger,"request","".concat(a," event")),(0,c.fieldRequiredLog)(t,r.logger,"event","".concat(a," event")),(0,c.fieldRequiredLog)(t,r.logger,"user","".concat(a," event"));var n=a==="risk"?V.adaptRequestInfoWithOptionalHeaders:V.adaptRequestInfoWithRequiredHeaders;(0,c.sanitize)(t,i,"request","request",n,r,V.FALLBACK_EVENT_REQUEST_INFO),(0,c.sanitize)(t,i,"event","event",He.adaptEventInfo,r,He.FALLBACK_EVENT_EVENT_INFO),(0,c.sanitize)(t,i,"user","user",Be.adaptUserInfo,r,Be.FALLBACK_EVENT_USER_INFO),(0,c.sanitizeIfNotNil)(t,i,"customFields","custom_fields",We.adaptCustomFields,r)}if(a==="risk"){(0,c.sanitize)(t,i,"config","config",x.adaptEvaluateConfig,r,x.FALLBACK_EVENT_EVALUATE_CONFIG);var u=i.config;(0,_e.isKeyExist)(u,"bypass_bot_validation")||(u.bypass_bot_validation=!0),(0,c.sanitizeIfNotNil)(t,i,"botbyeResult","botbye_result",_e.toString,r)}return a==="full"&&(0,c.sanitizeIfNotNil)(t,i,"config","config",x.adaptEvaluateConfig,r),i};G.adaptEvent=ut});var P=o(N=>{"use strict";Object.defineProperty(N,"__esModule",{value:!0});N.REQUEST_TIMEOUT_ERROR_CODE=N.BASE_REQUEST_TIMEOUT=N.REQUEST_ERROR_CODE=void 0;var ot="REQUEST_ERROR";N.REQUEST_ERROR_CODE=ot;var st="REQUEST_TIMEOUT";N.REQUEST_TIMEOUT_ERROR_CODE=st;var lt=1500;N.BASE_REQUEST_TIMEOUT=lt});var xe=o(Q=>{"use strict";Object.defineProperty(Q,"__esModule",{value:!0});Q.httpClientCallWithTimeout=void 0;var vt=v(),Oe=P(),ct=function(e,r,t,i){t===void 0&&(t=Oe.BASE_REQUEST_TIMEOUT);var a=(0,vt.withTimeout)(e.result,t,Oe.REQUEST_TIMEOUT_ERROR_CODE).catch(function(n){var u;throw n===Oe.REQUEST_TIMEOUT_ERROR_CODE&&(r.logger.debug("The request ".concat((u=i?.name)!==null&&u!==void 0?u:""," was interrupted by a timeout")),e.abort()),n});return{result:a,abort:e.abort}};Q.httpClientCallWithTimeout=ct});var Ge=o(J=>{"use strict";Object.defineProperty(J,"__esModule",{value:!0});J.EVALUATE_DECISIONS=void 0;var Et=["ALLOW","BLOCK","CHALLENGE"];J.EVALUATE_DECISIONS=Et});var M=o(d=>{"use strict";Object.defineProperty(d,"__esModule",{value:!0});d.SDK_ERROR=d.JSON_ERROR=d.CONNECTION_ERROR=d.TIMEOUT_ERROR=d.UNKNOWN_ERROR=void 0;var dt="unknown error";d.UNKNOWN_ERROR=dt;var ft="timeout";d.TIMEOUT_ERROR=ft;var _t="connection error";d.CONNECTION_ERROR=_t;var Ot="invalid json response";d.JSON_ERROR=Ot;var Rt="SDK error";d.SDK_ERROR=Rt});var j=o(Y=>{"use strict";Object.defineProperty(Y,"__esModule",{value:!0});Y.createFallbackEvaluationResult=void 0;var Nt=function(e){return{decision:"ALLOW",config:{bypass_bot_validation:!0},error:{message:e}}};Y.createFallbackEvaluationResult=Nt});var Je=o(X=>{"use strict";Object.defineProperty(X,"__esModule",{value:!0});X.withApiErrorHandle=void 0;var Qe=P(),p=v(),Tt=Ge(),C=M(),m=j(),gt=function(e){return!(!(0,p.isPlainObject)(e)||!("request_id"in e)||!(0,p.isString)(e.request_id)||!("decision"in e)||!(0,p.isString)(e.decision)||!Tt.EVALUATE_DECISIONS.includes(e.decision)||!("risk_score"in e)||!(0,p.isNumber)(e.risk_score)||!("signals"in e)||!Array.isArray(e.signals)||!("scores"in e)||!(0,p.isPlainObject)(e.scores)||!("config"in e)||!(0,p.isPlainObject)(e.config))},pt=function(e){return(0,p.isString)(e)?e:e instanceof Error?e.message:"UNKNOWN"},Lt=function(e,r){return new Promise(function(t){try{e().result.then(function(i){var a=null;try{a=JSON.parse(i)}catch{t((0,m.createFallbackEvaluationResult)(C.JSON_ERROR));return}if(!gt(a)){t((0,m.createFallbackEvaluationResult)(C.JSON_ERROR));return}t(a)}).catch(function(i){var a=pt(i);if(a.includes(Qe.REQUEST_TIMEOUT_ERROR_CODE)){t((0,m.createFallbackEvaluationResult)(C.TIMEOUT_ERROR));return}if(a.includes(Qe.REQUEST_ERROR_CODE)){t((0,m.createFallbackEvaluationResult)(C.CONNECTION_ERROR));return}t((0,m.createFallbackEvaluationResult)(C.UNKNOWN_ERROR))})}catch{t((0,m.createFallbackEvaluationResult)(C.SDK_ERROR))}})};X.withApiErrorHandle=Lt});var Ye=o(Z=>{"use strict";Object.defineProperty(Z,"__esModule",{value:!0});Z.evaluateFactory=void 0;var yt=Se(),ht=ze(),It=xe(),St=P(),At=v(),Ct=Je(),mt=j(),Ut=M(),qt=function(e){return function(r){return e.serverKey?(0,Ct.withApiErrorHandle)(function(){var t;return(0,It.httpClientCallWithTimeout)((0,yt.sendEvaluateCall)({global:e,event:(0,ht.adaptEvent)(r,e)}),e,(0,At.isNumber)((t=e.timeouts)===null||t===void 0?void 0:t.evaluate)?e.timeouts.evaluate:St.BASE_REQUEST_TIMEOUT,{name:"EVALUATE"})},e):(e.logger.error("serverKey is not set. Check the init function call."),Promise.resolve((0,mt.createFallbackEvaluationResult)(Ut.SDK_ERROR)))}};Z.evaluateFactory=qt});var Re=o($=>{"use strict";Object.defineProperty($,"__esModule",{value:!0});$.LOGGER_LEVELS=void 0;var Ft=["error","warn","info","debug","log"];$.LOGGER_LEVELS=Ft});var Ze=o(f=>{"use strict";var bt=f&&f.__spreadArray||function(e,r,t){if(t||arguments.length===2)for(var i=0,a=r.length,n;i<a;i++)(n||!(i in r))&&(n||(n=Array.prototype.slice.call(r,0,i)),n[i]=r[i]);return e.concat(n||Array.prototype.slice.call(r))};Object.defineProperty(f,"__esModule",{value:!0});f.withPackageName=f.withSafeMixins=f.withTryCatch=f.withLogLevel=void 0;var Vt=v(),ee=Re(),Xe=function(){},Ne=function(e,r){return ee.LOGGER_LEVELS.reduce(function(t,i){var a,n=((a=r[i])!==null&&a!==void 0?a:Xe).bind(r);return t[i]=e(i,n),t},r)},Pt=function(e){return ee.LOGGER_LEVELS.reduce(function(r,t){var i=(0,Vt.isObject)(e)?e[t]:void 0;return r[t]=(i??Xe).bind(e),r},{})};f.withSafeMixins=Pt;var Mt=function(e){var r=function(t,i){return function(){for(var a=[],n=0;n<arguments.length;n++)a[n]=arguments[n];i.apply(void 0,bt(["[BotBye!]"],a,!1))}};return Ne(r,e)};f.withPackageName=Mt;var jt=function(e){var r=function(a,n){return ee.LOGGER_LEVELS.indexOf(n)<=ee.LOGGER_LEVELS.indexOf(a)},t=function(a,n){return function(){for(var u=[],s=0;s<arguments.length;s++)u[s]=arguments[s];r(this.level,a)&&n.apply(void 0,u)}},i=Ne(t,e);return i.setLevel=function(a){this.level=a},i};f.withLogLevel=jt;var wt=function(e){var r=function(t,i){return function(){for(var a=[],n=0;n<arguments.length;n++)a[n]=arguments[n];try{i.apply(void 0,a)}catch{}}};return Ne(r,e)};f.withTryCatch=wt});var $e=o(U=>{"use strict";var re=U&&U.__assign||function(){return re=Object.assign||function(e){for(var r,t=1,i=arguments.length;t<i;t++){r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},re.apply(this,arguments)};Object.defineProperty(U,"__esModule",{value:!0});U.evaluateWithExtractorFactory=void 0;var Kt=v(),Dt=function(e){return(0,Kt.isKeyExist)(e,"request")},kt=function(e,r,t){return function(i){var a;if(Dt(i.request)){var n=e(i.request.request,t);i.request=re(re({},n),{token:(a=i.request.token)!==null&&a!==void 0?a:n.token})}return r(i)}};U.evaluateWithExtractorFactory=kt});var er=o(te=>{"use strict";Object.defineProperty(te,"__esModule",{value:!0});te.withAnyErrorHandle=void 0;var Ht=j(),Bt=M(),Wt=function(e){return function(r){return new Promise(function(t){try{t(e(r))}catch{t((0,Ht.createFallbackEvaluationResult)(Bt.SDK_ERROR))}})}};te.withAnyErrorHandle=Wt});var tr=o(_=>{"use strict";Object.defineProperty(_,"__esModule",{value:!0});_.isLoggerValid=_.isLoggerLevelValid=_.isTimeoutsValid=_.assertInitOptions=void 0;var q=v(),rr=Re(),zt=function(e){return(0,q.isPlainObject)(e)?rr.LOGGER_LEVELS.every(function(r){return typeof e[r]=="function"}):!1};_.isLoggerValid=zt;var xt=function(e){return!(!(0,q.isPlainObject)(e)||"evaluate"in e&&!(0,q.isNumber)(e.evaluate))};_.isTimeoutsValid=xt;var Gt=function(e){return(0,q.isString)(e)?rr.LOGGER_LEVELS.includes(e):!1};_.isLoggerLevelValid=Gt;var Qt=function(e){if(!(0,q.isString)(e.serverKey)||e.serverKey.length===0)throw new Error("initOptions.serverKey must be a non-empty string");if(e.url!=null&&(!(0,q.isString)(e.url)||e.url.length===0))throw new Error("initOptions.url must be a non-empty string")};_.assertInitOptions=Qt});var nr=o(ae=>{"use strict";Object.defineProperty(ae,"__esModule",{value:!0});ae.moduleApiFactory=void 0;var Jt=Ie(),Yt=Ye(),ie=Ze(),Xt=$e(),Te=v(),Zt=er(),w=tr(),ir=function(e){return(0,ie.withTryCatch)((0,ie.withLogLevel)((0,ie.withPackageName)((0,ie.withSafeMixins)(e))))},ar="info",$t=function(e){var r,t=ir(console);t.setLevel(ar);var i={url:(r=e.url)!==null&&r!==void 0?r:"https://verify.botbye.com",serverKey:"",module:e.module||{name:"NODE_CORE",version:"2.0.0"},httpClient:e.httpClient,logger:t},a=(0,Yt.evaluateFactory)(i);if(e.requestInfoExtractor){var n=e.requestInfoExtractor;a=(0,Xt.evaluateWithExtractorFactory)(n,a,i)}var u=(0,Te.once)(function(){(0,Jt.sendInitCall)({global:i}).result.then(function(){i.logger.info("Inited successfully")}).catch(function(s){i.logger.warn("Can't send init call: ".concat((0,Te.extractMessageFromError)(s)))})});return{init:(0,Te.once)(function(s){var K,oe;(0,w.assertInitOptions)(s),t=(0,w.isLoggerValid)((K=s.logger)===null||K===void 0?void 0:K.logger)?ir(s.logger.logger):t,i.url=s.url||i.url,i.serverKey=s.serverKey,i.logger=t,i.timeouts=(0,w.isTimeoutsValid)(s.timeouts)?s.timeouts:void 0,t.setLevel((0,w.isLoggerLevelValid)((oe=s.logger)===null||oe===void 0?void 0:oe.level)?s.logger.level:ar),e.skipInitCall||u()}),evaluate:(0,Zt.withAnyErrorHandle)(a),dev:{setLoggerLevel:function(s){(0,w.isLoggerLevelValid)(s)&&i.logger.setLevel(s)},sendInitCall:u,getLogger:function(){return t}}}};ae.moduleApiFactory=$t});var ur=o(O=>{"use strict";Object.defineProperty(O,"__esModule",{value:!0});O.SDK_ERROR=O.createFallbackEvaluationResult=O.getIpFromHeaders=O.moduleApiFactory=void 0;var ei=nr();Object.defineProperty(O,"moduleApiFactory",{enumerable:!0,get:function(){return ei.moduleApiFactory}});var ri=v();Object.defineProperty(O,"getIpFromHeaders",{enumerable:!0,get:function(){return ri.getIpFromHeaders}});var ti=j();Object.defineProperty(O,"createFallbackEvaluationResult",{enumerable:!0,get:function(){return ti.createFallbackEvaluationResult}});var ii=M();Object.defineProperty(O,"SDK_ERROR",{enumerable:!0,get:function(){return ii.SDK_ERROR}})});var or=o(ne=>{"use strict";Object.defineProperty(ne,"__esModule",{value:!0});ne.bodyToJson=void 0;var ai=function(e){if(e!=null)return typeof e=="string"?e:JSON.stringify(e)};ne.bodyToJson=ai});var lr=o(ue=>{"use strict";Object.defineProperty(ue,"__esModule",{value:!0});ue.fetchHttpClient=void 0;var ni=v(),ui=or(),sr=P(),oi={type:"fetchHttpClient",call:function(e,r){var t=new AbortController,i=function(){t.abort()},a=fetch(e,{method:r.method,body:(0,ui.bodyToJson)(r.body),headers:r.headers,keepalive:r.keepalive,signal:t.signal}).then(function(n){if(!n.ok)throw new Error(sr.REQUEST_ERROR_CODE);return n.text()}).catch(function(n){throw new Error("".concat(sr.REQUEST_ERROR_CODE," ").concat((0,ni.extractMessageFromError)(n)))});return{result:a,abort:i}}};ue.fetchHttpClient=oi});var ci={};gr(ci,{dev:()=>vi,evaluate:()=>li,factory:()=>fr,init:()=>si});var Er=pe(ur()),dr=pe(lr());var vr="CF_WORKER",cr="2.0.0";var fr=()=>{let e=(0,Er.moduleApiFactory)({httpClient:dr.fetchHttpClient,module:{name:vr,version:cr},skipInitCall:!0,requestInfoExtractor:(t,i)=>{try{let a={};return t.headers.forEach((n,u)=>{a[u]=n}),{ip:t.headers.get("CF-Connecting-IP")??"0.0.0.0",requestUri:new URL(t.url).pathname,requestMethod:t.method,headers:a}}catch{return i.logger.warn("Not valid type of request passed. event.request.request should be an instance of CF Workers Request. Fallback value will be used"),{ip:"0.0.0.0",headers:{}}}}}),r=!1;return{...e,evaluate:(...t)=>(r||(e.dev.sendInitCall(),r=!0),e.evaluate(...t))}},{init:si,evaluate:li,dev:vi}=fr();return pr(ci);})();

Configuration

Call init at module load time, before the fetch handler runs:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { init } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { init } = BotBye;

init({
  serverKey: "00000000-0000-0000-0000-000000000000", // Use your project server-key
});

export default {
  async fetch(request, env, ctx) {
    // ...
  },
};

init() itself makes no network calls — it only stores configuration. The connection handshake with BotBye is deferred to the first evaluate() call.

init options

Option Type Required Description
serverKey string Yes Server key from your BotBye project
url string No Override BotBye API endpoint (default: https://verify.botbye.com)
logger.level string No Log level: "error", "warn", "info", "debug", "log" (default: "info")
logger.logger TLogger No Custom logger instance implementing { error, warn, info, debug, log }
timeouts.evaluate number No Timeout in milliseconds for each evaluate call

Usage

Call evaluate inside the fetch handler where bot protection is needed. Pass an event object describing the request — the SDK returns a decision.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;

export default {
  async fetch(request, env, ctx) {
    const result = await evaluate({
      type: "validate",
      request: {
        request,
        // "x-botbye-token" is an example — pass the token from wherever you store it
        token: request.headers.get("x-botbye-token"),
      },
    });

    if (result.decision === "BLOCK") {
      return new Response(JSON.stringify({ error: "Forbidden" }), {
        status: 403,
        headers: { "Content-Type": "application/json" },
      });
    }

    // proceed normally
  },
};

There are three event types — validate, risk, and full — each suited for a different layer of your application.

validate — edge-level bot check

Use when you just want to know: was this request made by a bot? Pass the CF Workers Request object directly — the SDK extracts IP, headers, method, and URI automatically.

Event fields:

1
2
3
4
5
6
7
8
9
10
11
12
{
  type: "validate";

  request:
    // Option A: pass the CF Workers Request object directly — SDK extracts everything automatically
    | { request: Request; token?: string | null }
    // Option B: construct request info manually
    | { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };

  customFields?: Record<string, string>;

}

The SDK extracts IP, headers, method, and URI from the CF Workers Request object automatically. You can also pass request info manually — see Option B above. The token is a one-time token generated by the BotBye client-side SDK that contains information about the user's device. Pass whatever the client sent; if no token is received, the decision will be "BLOCK".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;

export default {
  async fetch(request, env, ctx) {
    const result = await evaluate({
      type: "validate",
      request: {
        request,
        // "x-botbye-token" is an example — pass the token from wherever you store it
        token: request.headers.get("x-botbye-token"),
      },
    });

    if (result.decision === "BLOCK") {
      return new Response(JSON.stringify({ error: "Forbidden" }), {
        status: 403,
        headers: { "Content-Type": "application/json" },
      });
    }

    // proceed normally
  },
};

risk — domain-level risk scoring

Use when you can read user context from the request (e.g. on a login endpoint). Send event.type, event.status, and user.accountId alongside the request to get a domain-level risk score.

Event fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  type: "risk";

  request:
    // Only ip is needed at this level
    | { ip: string; headers?: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null }
    // CF Workers Request also accepted if convenient
    | { request: Request };

  event: {
    type: string;   // e.g. "login", "password_change", "checkout"
    status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
  };

  user: {
    accountId: string;
    username?: string | null;
    email?: string | null;
    phone?: string | null;
  };

  customFields?: Record<string, string>;
  botbyeResult?: string; // if a validate call was made earlier, pass its result.botbyeResult here to link the requests; omit if there was no prior validate

}

event and user are the key fields here — they define what action is being performed and who is performing it, which is what drives the risk score. ip is equally important: BotBye tracks which IPs access the account to detect patterns like account sharing, credential stuffing, and suspicious geo logins. Pass it directly as { ip }, or pass the CF Workers Request object if that's more convenient.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;

export default {
  async fetch(request, env, ctx) {
    const { email } = await request.clone().json();

    const result = await evaluate({
      type: "risk",
      request: { request },
      event: {
        type: "login",
        status: "ATTEMPTED",
      },
      user: {
        accountId: email,
        email,
      },
    });

    if (result.decision === "BLOCK") {
      return new Response(JSON.stringify({ error: "Forbidden" }), {
        status: 403,
        headers: { "Content-Type": "application/json" },
      });
    }

    return fetch(request);
  },
};

Linking validate and risk events

When the same request is evaluated at two layers — for example, once at the edge (type: "validate") and then again inside a domain service (type: "risk") — BotBye can link both events and display them as a single event in the dashboard.

Step 1 — CF Worker (edge): run validate and capture botbye_result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
  async fetch(request, env, ctx) {
    const edgeResult = await evaluate({
      type: "validate",
      request: {
        request,
        // "x-botbye-token" is an example — pass the token from wherever you store it
        token: request.headers.get("x-botbye-token"),
      },
    });
    const edgeBotbyeResult = edgeResult.botbye_result;
    // Pass edgeBotbyeResult downstream — a custom request header, KV, function argument, etc.
  },
};

Step 2 — domain service (auth, payment, account management): pass it as botbyeResult in the risk call:

1
2
3
4
5
6
7
8
9
10
11
12
13
const riskResult = await evaluate({
  type: "risk",
  request: { request },
  event: {
    type: "login",
    status: "ATTEMPTED",
  },
  user: {
    accountId: email,
    email,
  },
  botbyeResult: edgeBotbyeResult,
});

botbye_result is optional in the response — if it is absent, omit botbyeResult and the events will be recorded independently.

full — edge check and domain scoring in one call

Use when the Worker itself is the endpoint and you have all context at once: the raw request with token, the user from the body, and the outcome of the action.

Event fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  type: "full";

  request:
    | { request: Request; token?: string | null }
    | { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };

  event: {
    type: string;
    status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
  };

  user: {
    accountId: string;
    username?: string | null;
    email?: string | null;
    phone?: string | null;
  };

  customFields?: Record<string, string>;

}

Equivalent to running validate and risk in a single call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;

export default {
  async fetch(request, env, ctx) {
    const { email, password } = await request.json();
    const user = await findUser(email, env);
    const loginSucceeded = user && (await checkPassword(user, password));

    const result = await evaluate({
      type: "full",
      request: {
        request,
        token: request.headers.get("x-botbye-token"),
      },
      event: {
        type: "login",
        status: loginSucceeded ? "SUCCESSFUL" : "FAILED",
      },
      user: {
        accountId: user?.id ?? email,
        email,
      },
    });

    if (result.decision === "BLOCK") {
      return new Response(JSON.stringify({ error: "Forbidden" }), {
        status: 403,
        headers: { "Content-Type": "application/json" },
      });
    }

    // proceed normally
  },
};

Response

evaluate always returns a Promise<TEvaluationResult>:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type TEvaluationResult =
  | {
      decision: "ALLOW" | "BLOCK" | "CHALLENGE";
      request_id: string;
      risk_score: number;
      scores: Record<string, number>;
      signals: string[];
      botbye_result?: string;
    }
  | {
      decision: "ALLOW";
      botbye_result?: string;
      error: { message: string };
    };

Check result.decision to decide how to handle the request:

  • "ALLOW" — request appears legitimate, proceed normally
  • "BLOCK" — bot or suspicious activity detected, block the request
  • "CHALLENGE" — uncertain, consider issuing a CAPTCHA, MFA, or additional verification step

When the response contains an error field, BotBye could not evaluate the request (e.g. invalid server key). In that case decision defaults to "ALLOW" so that a misconfiguration does not block real users — but you should monitor and fix the underlying error.

Examples of BotBye API responses

Blocked (bot detected):

1
2
3
4
5
6
7
{
  "request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
  "decision": "BLOCK",
  "risk_score": 0.95,
  "scores": { "bot": 0.95 },
  "signals": ["AutomationTool"]
}

Allowed:

1
2
3
4
5
6
7
{
  "request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
  "decision": "ALLOW",
  "risk_score": 0.05,
  "scores": { "bot": 0.05, "ato": 0.02 },
  "signals": []
}

Challenge:

1
2
3
4
5
6
7
8
{
  "request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
  "decision": "CHALLENGE",
  "risk_score": 0.65,
  "scores": { "bot": 0.65 },
  "signals": ["SuspiciousFingerprint"],
  "challenge": { "type": "captcha", "token": "..." }
}

Invalid server-key:

1
2
3
4
{
  "decision": "ALLOW",
  "error": { "message": "[BotBye] Bad Request: Invalid Server Key" }
}

Advanced: multiple instances

Use factory to create independent SDK instances (useful when protecting multiple projects from one Worker):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { factory } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { factory } = BotBye;

const sdk = factory();

sdk.init({
  // Use your project server-key
  serverKey: "00000000-0000-0000-0000-000000000000",
});

const result = await sdk.evaluate({
  type: "validate",
  request: { request, token },
});

Dev utilities

1
2
3
4
5
import { dev } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { dev } = BotBye;

dev.setLoggerLevel("debug"); // "error" | "warn" | "info" | "debug" | "log"