Evaluate a flow against your rules
Beta. The request and response shapes may still change. Talk to us before you build against it. Flows, recipes and rules are authored through the Watch Management API, or configured by Prelude on your behalf.
Score a target against the rules configured for one moment in your product — signup, checkout, password reset. The flow selects which recipes run; each recipe scores its rules against a threshold and returns its own verdict, and the evaluation answers with the most severe verdict across them. The verdict is advisory: what to do about it is yours. Where Predict returns a single model-derived outcome, Eval returns the full breakdown, so you can see which rules fired and which could not run.
flow_id to the flow configured for that moment, and target to the end user you are evaluating - phone_number, email_address, or both. At least one is required; send both when you hold both, since a rule may read either.
A flow selects which recipes run. Each recipe scores its rules against a threshold and returns its own verdict. The evaluation answers with the most severe verdict across all of them.
Eval is scoring-only and advisory. It doesn’t update long-lived abuse counters by itself, and the verdict is a recommendation: enforcement is yours.
Eval compared with Predict
Both score an identifier. They differ in what they return and what you control.Reading the response
Start atrecipes[].verdict and work down. A recipe flags when its score reaches its threshold. An overriding rule can settle the verdict first, and that case sets determined_by. It’s the only thing that accounts for a recipe reporting a score under its threshold and flagging anyway.
partial_evidence means at least one rule couldn’t run, so the score rests on less than the whole recipe. Each such rule reports outcome: NOT_EVALUATED with a blocked_by naming what it waited on. This isn’t a quieter NOT_TRIGGERED: the rule contributed nothing in either direction. A PASS carrying partial_evidence is weaker evidence than a PASS without it.
The usual cause is a signal that never arrived. Passing dispatch_id from the front-end SDK fills in most of what rules read. Without it, any rule reading a device or network signal reports NOT_EVALUATED.
SKIPPED is different. Once an overriding rule has determined a recipe’s verdict, a remaining rule that could no longer change it may not be run at all. Such a rule reports its weight and outcome: SKIPPED, and the recipe does not set partial_evidence — determined_by already accounts for the verdict.
Rules you didn’t write
Your recipes can include Prelude-managed rules alongside your own. A managed rule returns itsrule_id, weight, and outcome, but not its name. It reports blocked_by: "missing_data" rather than naming the signal it needed, because its condition isn’t disclosed.
The identifier is always returned, so a rule whose condition you can’t see is still one you can reweight, switch off, or ask us about.
The target
The two halves oftarget are also readable from a rule, as target.phone_number and target.email_address - see Rules, recipes and flows. The values a rule compares against are the canonical ones: the number in E.164, the address normalized.
The half you omit is missing evidence, not an empty value. A rule reading it reports NOT_EVALUATED rather than being scored as though the condition were false, exactly as it would for a signal that never arrived. Sending both halves is therefore what makes rules about either of them run.
Signals
Send the values your rules read insignals:
- The SDK signals the API accepts, such as
ipordevice_platform. A rule reads them assignal.ip,signal.device_platform, and so on. When the front-end SDK also provided a value throughdispatch_id, the SDK value is used. - Your own signals, under
ext: a plan tier, an account’s age, a cart total. A rule readsext.plan_tierassignal.ext.plan_tier. Values are strings.
NOT_EVALUATED rather than being scored as though the condition were false. Sending it with an empty value is the same as omitting it. A key no rule in the flow reads is ignored rather than rejected, so one payload can serve flows that read different keys.
To have a rule read your own value for an SDK signal, for example the IP address your backend saw, send it under ext too (ext.ip) and read it as signal.ext.ip.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
The flow to evaluate. A flow names the moment you are guarding and selects the recipes that run.
"flo_01jc0t6fwwfgfsq1md24mhyztj"
The end user being evaluated. Send a phone number, an email address, or both — at least one is required, and sending both is what to do when you hold both.
Each value is also readable from a rule, as target.phone_number and target.email_address. The half this request omits is missing evidence: rules reading it report NOT_EVALUATED rather than being scored against an empty string.
The values the flow's rules read. A rule reads an SDK signal as signal.<name>; when the front-end SDK also provided it through dispatch_id, the SDK value is used.
The identifier of the dispatch that came from the front-end SDK. Its signals are used first; an SDK signal in signals is used only when the dispatch did not provide it.
36"123e4567-e89b-12d3-a456-426614174000"
Response
OK
The evaluation identifier.
"evl_01jc0t6fwwfgfsq1md24mhyztj"
The evaluation-level verdict, being the most severe verdict across the recipes that ran.
PASS- No recipe flagged.FLAG- At least one recipe flagged.
PASS, FLAG One result per recipe that ran. A recipe the flow names but that is not in service is absent rather than reported as having passed.