Skip to main content
POST
Evaluate a flow
Eval is in beta. The request and response shapes may still change. Talk to us before you build against it.
Flows, recipes and rules are yours to author with the Management API - see Rules, recipes and flows for the model. Prelude can also configure them on your behalf. Call Eval at any moment you want to guard: signup, checkout, password reset, a payout request. Set 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. Use Predict when you want an answer. Use Eval when you want to see the reasoning, tune it, or add conditions of your own.

Reading the response

Start at recipes[].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 its rule_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 of target 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 in signals:
  • The SDK signals the API accepts, such as ip or device_platform. A rule reads them as signal.ip, signal.device_platform, and so on. When the front-end SDK also provided a value through dispatch_id, the SDK value is used.
  • Your own signals, under ext: a plan tier, an account’s age, a cart total. A rule reads ext.plan_tier as signal.ext.plan_tier. Values are strings.
One of your own signals that a rule reads and your request omits counts as missing evidence, not an empty value. Rules reading it report 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

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
flow_id
string
required

The flow to evaluate. A flow names the moment you are guarding and selects the recipes that run.

Example:

"flo_01jc0t6fwwfgfsq1md24mhyztj"

target
object
required

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.

signals
object

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.

dispatch_id
string

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.

Required string length: 36
Example:

"123e4567-e89b-12d3-a456-426614174000"

Response

OK

id
string
required

The evaluation identifier.

Example:

"evl_01jc0t6fwwfgfsq1md24mhyztj"

verdict
enum<string>
required

The evaluation-level verdict, being the most severe verdict across the recipes that ran.

  • PASS - No recipe flagged.
  • FLAG - At least one recipe flagged.
Available options:
PASS,
FLAG
recipes
object[]
required

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.