Skip to main content
Some tool calls should not run without a human in the loop - issuing a refund, deleting data, sending money. Tool approval posts an Approve / Deny card in the conversation and pauses the turn until the user decides. This page covers ctx.toolApproval.request(), the onToolApproval handler, and card customization. How you gate a tool depends on your runtime:
Set needsApproval: true on an AI SDK tool. When the model calls it, Novu posts the approval card and pauses the turn. After the user clicks Approve or Deny, Novu re-runs onMessage so generateText continues the tool loop - no ctx.toolApproval.request() needed.
See AI SDK for auto-resume and optional onToolApproval hooks.

Default behavior

When you do not register an onToolApproval handler, Novu handles the click for you:
  • The user clicks Approve or Deny.
  • Novu deletes the approval card and restarts the typing indicator. See Typing indicator.
  • The decision is recorded in ctx.history.
If several tools need approval in the same turn, they surface one at a time - the next card appears after the current one is resolved.
If the user sends a new message while a card is pending, Novu auto-denies the pending tool(s), deletes the card, and processes the new message.
This is the simplest path - post the card and let Novu clean it up. On the Custom code path, handle the decision in onToolApproval when you need to run the tool or post a reply after the click.

Handle the decision with onToolApproval

Register onToolApproval when you need to act on the click - run the tool, post a result, edit the card, or audit the decision. It receives a decision:
When you register onToolApproval, you own card cleanup. Novu no longer auto-deletes the card - call decision.approvalMessage.edit() or decision.approvalMessage.delete() yourself.
The decision object:

Edit the card in place

Instead of deleting, edit the card to reflect the outcome so the thread keeps a record:

Customize the card

By default Novu renders an Approve / Deny card. Customize it with toolApproval.renderApproval. Novu always supplies the action ids and wires the buttons unless you build a fully custom card.
renderApproval receives: approvalCard() fields (channel support noted): For a fully custom card, wire the action ids onto your own buttons:

AI SDK

Automatic gating with needsApproval: true and auto-resume.

LangChain

Automatic gating with needsApproval callback and auto-resume.

Edit sent messages

Edit and delete cards in place with ReplyHandle.

Handlers and context

All handler events and the context object.