Skip to main content
A preprocessor runs your code on every incoming message before the model sees it, and a postprocessor runs on every reply before the end user sees it. They exist for rules that must hold in every conversation regardless of what the model decides: blocking, rate limits, redaction, enrichment, a disclaimer, channel-specific formatting.

How a processor runs

Both are versioned primitives registered on LuaAgent as preProcessors and postProcessors, pushed with lua push preprocessor --name <name> or lua push postprocessor --name <name>, and made live with the matching lua deploy type or an agent version promote.
src/preprocessors/officeHours.ts
A preprocessor’s execute receives the end user, the message as an array of parts (text, image, or file), and the channel. It returns { action: 'proceed' }, optionally with modifiedMessage, a replacement array of parts that the next preprocessor and then the model receive, or { action: 'block', response }. A block ends the turn: the model never runs, and response is sent to the end user as the reply and stored in the conversation. Leave response empty and what the end user sees depends on the client: the legacy stream shows nothing, the widget’s stream shows the default text User not eligible for agent response, and a non-streaming chat call returns a preprocessor_blocked result with empty text. Set response whenever the end user should know why. metadata is accepted on both results but is not passed anywhere. A postprocessor’s execute receives the end user, the original message, the model’s reply, and the channel, and must return { modifiedResponse }; that text replaces the reply. Active processors of each kind run one after another in ascending priority (default 100), each receiving the previous one’s output. Two processors that share a priority run in the order the database returns them, which you cannot set, so give each a distinct number. async is accepted on a preprocessor but nothing reads it; every preprocessor runs in sequence before the model. Failure is handled per channel. A preprocessor that throws or times out is skipped on text channels and the message continues unchanged; on a voice call the turn is blocked instead, with the spoken reply Sorry, I'm having trouble right now. Could you try that again?, so the caller never hears silence. A postprocessor that throws is skipped on every channel and the original reply is sent. On streaming channels the end user sees the reply as the model produces it. Postprocessors run when the stream ends, and only if they changed the text does the client receive a postprocess-complete event with the final version; the stored reply is the post-processed one. When the end user must never see the unprocessed text, use the non-streaming chat endpoint. lua test preprocessor --name <name> --input '{"message":"hello","channel":"web"}' and lua test postprocessor --name <name> --input '{"message":"hi","response":"Hello","channel":"web"}' run one processor on your machine. lua preprocessors and lua postprocessors view, activate, deactivate, deploy, and delete them, named with --preprocessor-name <name> or --postprocessor-name <name>.

Processors and skills

For one tool’s output, transform it inside the tool. For a rule the organization sets rather than you, or for gating tool calls, see governance, which runs before your preprocessors.

When to use it

  • A rule must hold for every message: profanity, office hours, per-user rate limits, a handoff flag that pauses the agent while a person talks.
  • Context the model always needs: append the end user’s account status to the message.
  • Every reply needs the same treatment on a channel: a footer, a length cap, WhatsApp-safe formatting.
  • Don’t use one for a decision the model should make with a tool, or for a transform that applies to a single tool.

Limits

Next steps

Add a processor

Write, test, push, and deploy a preprocessor or postprocessor.

PreProcessor reference

Config fields and the result shape.

PostProcessor reference

Config fields and the response shape.

Add human handoff

Pause the agent with a flag and a blocking preprocessor.