Implementation Guide

    Holding LLM Responses for Human Review in Real-Time Chat Without Timing Out

    Treat the hold as state, not as an open connection. Persist the held response server-side, give the user a resumable session that receives status events, and let a separate approval component record a decision. Release only when a decision record exists. If the reviewer SLA expires, fail closed: deliver a neutral message, never the held text. Keep-alive alone suits only short, predictable reviews.

    Decision evidence to capture for every held response

    • Original model output and the user request that produced it
    • The policy or threshold that triggered the hold, with timestamp
    • Reviewer assignments, reassignments and escalations with timestamps
    • Reviewer identity, decision (approved, edited, rejected, expired) and decision time
    • For edits, both the original and the edited text, and the reviewer's reason where required
    • The exact content delivered to the user, delivery time, and the fallback message if one was used

    Why a held response breaks a live session

    Chat transports assume a response arrives soon. Human review breaks that assumption, because reviewer latency depends on queue depth, shift patterns and case complexity, none of which the transport knows about.

    The session can fail at several independent layers. The client may enforce a request timeout. A WebSocket or server-sent events stream may be closed after an idle period. A load balancer or API gateway may cut a connection that carries no traffic. The application server may reclaim the worker. Each layer has its own configured limit, often owned by a different team, and the shortest one decides how long a connection can safely stay open. Inventory these limits in your own environment before choosing a pattern.

    There is also a human layer. A user waiting on a silent screen may refresh, retry or leave. A retry can create a duplicate request and a second held response unless the system treats requests idempotently.

    The design goal is therefore two-part: keep the user informed or able to return, and guarantee that nothing is released without a recorded decision, whatever happens to the connection.

    Three patterns for holding a response

    These patterns are not mutually exclusive. Most production designs combine asynchronous decoupling with a timeout fallback, and use status events for the live portion of the wait.

    • Keep-alive with status events: The connection stays open while the server sends periodic status or heartbeat events such as 'awaiting review'. Simple for the client and the experience feels continuous. The cost is that it depends on every layer tolerating a long-lived connection, and a dropped connection loses the wait unless state is stored. Suited to reviews expected to finish quickly.
    • Asynchronous decoupling with resumable sessions: The request returns an acknowledgement and a reference to the pending item. The held response lives in durable storage. The client reconnects, polls or subscribes for the outcome, and resumes from the stored session. More components and client logic, but the wait is no longer bound to any connection limit. Suited to unpredictable or long reviews.
    • Timeout fallback: A deadline attached to the held item triggers a defined behavior when review does not finish: a fail-closed message, reassignment, or a safe default response. It adds no waiting capacity, but it bounds the wait and defines what the user receives. Required alongside either pattern above.

    Choosing a pattern for your transport and latency profile

    Start from the review latency you can actually commit to. If reviewers reliably decide within seconds and your shortest intermediary limit is comfortably longer, keep-alive with status events is the least complex option. Add heartbeats so intermediaries see traffic, and still persist the held item so a reconnect does not lose it.

    If latency is variable, or the review may span minutes or longer, decouple. Return immediately, show the user a pending state, and deliver the outcome when the client resumes. For WebSocket or SSE clients, that means reconnecting with a session identifier and receiving the current state of the item. For plain HTTP clients, polling a status endpoint works and is easy to reason about through proxies.

    In both cases, keep the held content out of the client. The user sees only status, never the withheld text. A client that holds the response and merely hides it can leak it through the browser, a cache or a debugging tool, which defeats the purpose of the hold.

    Use idempotency keys on user messages so a retry attaches to the existing pending item rather than creating another.

    Separate the session layer from the decision layer

    Model the hold as a small state machine owned by the decision layer, not by the chat connection. A workable set of states is generated, held, under review, approved, edited, rejected, expired, and delivered. Transitions are written to durable storage before they take effect.

    The session layer only reads state and renders it. It never decides what is releasable. The release path has one invariant: it delivers content only if the item is in an approved or edited state with a recorded decision, and it delivers exactly the content that decision refers to. Any other state, including an unknown or unreachable one, results in no delivery. That is what fail closed means in practice.

    This separation matches the direction of NIST guidance on agent identity, which describes technology components that trigger escalation to the correct authority when organization-defined risk thresholds are reached. NIST also stresses that escalation should carry enough information for a human to decide, which may go beyond the condition that triggered it. Build the reviewer view accordingly: the original output, the user request, the policy that caused the hold and relevant conversation context.

    Reviewer SLAs, expiry and fallback behavior

    Set the SLA per hold reason, because risk and urgency differ. Define a time to first assignment and a time to decision, and attach a hard expiry to every held item at creation. Expiry should not be an afterthought handled by a cleanup job.

    Decide in advance what happens at each deadline. A common progression is reassignment to another reviewer, then escalation to a supervisor, then expiry. At expiry the default is to reject the held output and tell the user plainly that the response could not be delivered and what to do next, such as waiting for a follow-up or using another channel. Do not release the original output on timeout, and do not substitute an unreviewed generated answer. A pre-approved static message is acceptable because it was reviewed when written.

    NIST's GenAI Profile draft recommends policies and procedures to test and manage the risks of rollover and fallback technologies, and notes that fallback may include manual processing. Treat fallback paths as something you test: simulate reviewer outages, queue floods and dropped connections, and confirm each ends in a recorded outcome.

    Watch escalation volume as well. NIST warns of consent fatigue, where frequent escalation leads to habitual approval. Track hold rate, time to decision and approval rate per reviewer, and tighten the triggers if approvals become reflexive.

    Where runtime policy enforcement fits

    The hold only works if it sits in the execution path. A review step that runs beside the path, or reviews after delivery, cannot prevent release. NIST makes a related point for agents: without runtime verification, an agent holding a valid token cannot be interrupted mid-task, so checks belong at each interaction or tool call and not only at session start.

    Trussed enforces policy in the execution path at the level of individual interactions and tool calls, with actions that include allow, redact, block, rate limit and escalate for human approval. Enforcement is at runtime, not reviewed after the fact, and it generates audit-ready evidence mapped to frameworks such as HIPAA, the EU AI Act and NIST. It can be self-managed in the customer cloud, with configurable retention and data residency. The session and transport design described above remains your application's responsibility.

    Whichever tooling you use, test the same property: with the reviewer unavailable and the connection dropped, can any path release a held response without a recorded decision?

    Lifecycle of a held response

    Held

    Output is stored server-side and withheld from the user.

    Under review

    A reviewer receives the output with enough context to decide.

    Decided

    Approved, edited or rejected, with identity and timestamp recorded.

    Delivered or expired

    Release requires a decision record. Expiry fails closed.

    Review your hold-for-approval design

    Walk through your transport limits, reviewer SLAs and evidence requirements with an expert, and see how enforcement in the execution path produces a decision record for each interaction.

    Talk to an Expert