Scoring

Score function runs and steps to track how well your workflows perform over time.

Scoring is in beta. Install the latest SDK with npm install inngest@latest.


inngest.score(options)

Score a run or step. Can be called inside or outside a function.

  • Name
    name
    Type
    string
    Required
    required
    Description

    A label for the score. Use consistent names across runs for aggregation. Examples: "accuracy", "user-feedback", "guardrail-pass".

  • Name
    value
    Type
    number | boolean
    Required
    required
    Description

    The score value. A finite number or a boolean. Use 0-1 for percentages, integers for counts, booleans for pass/fail guardrails, or any range that fits your use case.

  • Name
    runId
    Type
    string
    Required
    optional
    Description

    The run to score. Required when scoring from outside a function. Omit when scoring the current run from inside a function.

  • Name
    stepId
    Type
    string
    Required
    optional
    Description

    The step to score. Optional. When provided, the score attaches to that specific step in the trace view.

// Score the current run (inside a function)
await inngest.score({ name: "accuracy", value: 0.9 });

// Score a specific run (from anywhere)
await inngest.score({ name: "accuracy", value: 0.9, runId: "01ABC..." });

// Score a specific step
await inngest.score({
  name: "accuracy",
  value: 0.9,
  runId: "01ABC...",
  stepId: "generate-summary",
});

inngest.score.experiment(options)

Attribute a score to the experiment variant that produced a result. group.experiment() returns an experimentRef, which identifies the experiment and the variant that was served. Pass it here to credit the score to that variant.

The signal you want to score often arrives much later, from somewhere else entirely: a click, a rating, an LLM-judge verdict. Because you pass the experimentRef yourself, you can credit any of these back to the variant that produced the output, even hours later from a separate run.

  • Name
    name
    Type
    string
    Required
    required
    Description

    Score label.

  • Name
    value
    Type
    number | boolean
    Required
    required
    Description

    Score value. A finite number or a boolean.

  • Name
    experiment
    Type
    ExperimentRef
    Required
    required
    Description

    The experimentRef returned by group.experiment(). Identifies the experiment and the served variant. Shape: { experimentName: string, variant: string }.

  • Name
    runId
    Type
    string
    Required
    optional
    Description

    The run to attach the score to. Required when scoring from outside the run that produced the result; omit to use the current run.

Scoring an experiment from a different run than the one that served the variant? Pass that run's runId so the score shows up in the experiment view — otherwise it attaches to the run you're scoring from and won't appear under the experiment.

// Inside the function that runs the experiment
const { result, experimentRef } = await group.experiment("summary-strategy", {
  variants: { /* ... */ },
  select: experiment.weighted({ questionLed: 50, factLed: 50 }),
});

// Persist experimentRef + runId (e.g. on your record) so later runs can score it

// From a separate, later run (a click, rating, or deferred judge)
await inngest.score.experiment({
  name: "clickthrough",
  value: 1,
  experiment: experimentRef, // restored from your store
  runId: summarizeRunId,
});

step.score(id, options)

Score the current run from within a step context, as a durable, memoized step. runId is automatically set to the current run. Requires scoreMiddleware() (from inngest/experimental) on the client.

  • Name
    id
    Type
    string
    Required
    required
    Description

    A durable step ID used to memoize this score write, so a replay or retry can't write it twice. Must be unique within the run.

  • Name
    options
    Type
    object
    Required
    required
    Description
    Properties
    • Name
      name
      Type
      string
      Required
      required
      Description

      Score label.

    • Name
      value
      Type
      number | boolean
      Required
      required
      Description

      Score value. A finite number or a boolean.

await step.score("score-guardrail-pass", { name: "guardrail-pass", value: true });

createScorer(client, config, handler)

Create a reusable deferred scorer: a separate function, triggered via defer(), that evaluates a result without blocking or slowing the run that produced it. Use it when scoring is expensive or slow, such as an LLM-as-a-judge call or waiting on a signal that arrives later.

Imported from inngest/experimental.

  • Name
    client
    Type
    Inngest
    Required
    required
    Description

    Your Inngest client instance.

  • Name
    config
    Type
    object
    Required
    required
    Description
    Properties
    • Name
      id
      Type
      string
      Required
      required
      Description

      Unique identifier for the scorer function.

    • Name
      schema
      Type
      StandardSchema
      Required
      optional
      Description

      A Standard Schema (e.g. Zod) describing the input data the scorer expects. Optional; when set, the data passed to defer() is validated against it and the handler's event.data is typed from it.

  • Name
    handler
    Type
    function
    Required
    required
    Description

    Async function that receives event, step, and parents. Return { name, value } and the score is written for you, attributed to the parent run, and to the served variant when the scorer was deferred with an experiment ref. Return null or undefined to write nothing.

The handler's event.data is the payload sent via defer(), typed from schema. The simplest scorer returns one score and lets the SDK attribute it:

import { createScorer } from "inngest/experimental";
import { z } from "zod";

export const verbosityScorer = createScorer(
  inngest,
  { id: "verbosity-scorer", schema: z.object({ text: z.string() }) },
  async ({ event }) => {
    const wordCount = event.data.text.split(" ").length;
    return { name: "verbosity", value: wordCount };
  }
);

To emit several scores, or to attribute explicitly, write them yourself with inngest.score.experiment(). The parent run and its served variant are on ctx.parents[0], so there's no need to persist them:

export const judgeScorer = createScorer(
  inngest,
  { id: "judge-summary", schema: z.object({ summary: z.string() }) },
  async ({ event, step, parents }) => {
    const judged = await step.run("judge", () => judge(event.data.summary));

    const { experiment, runId } = parents[0];
    await inngest.score.experiment({ name: "faithfulness", value: judged.faithfulness, experiment, runId });
    await inngest.score.experiment({ name: "spoiler", value: judged.spoiler, experiment, runId });

    return null; // scores already written above
  }
);

defer(id, options)

Trigger a deferred scorer from inside a function. Available as a handler argument.

  • Name
    id
    Type
    string
    Required
    required
    Description

    A deterministic identifier for this deferred call.

  • Name
    options
    Type
    object
    Required
    required
    Description
    Properties
    • Name
      function
      Type
      InngestFunction
      Required
      required
      Description

      The scorer function to trigger.

    • Name
      data
      Type
      object
      Required
      required
      Description

      Input data matching the scorer's schema.

    • Name
      experiment
      Type
      ExperimentRef
      Required
      optional
      Description

      The experimentRef from group.experiment(). Pass it to attribute the scorer's result to the served variant; surfaced on the scorer's ctx.parents[0].experiment.

defer() is fire-and-forget: it returns void, so there's nothing to await.

defer("score-feedback", {
  function: feedbackScorer,
  data: { ticketId: "tk_123" },
  experiment: experimentRef, // optional; credits the score to the served variant
});

Further reading