Human-in-the-loop for support agents

Your support agent should not issue that refund alone.

The escalation reaches a person on their phone and comes back as an answer, not a ticket in a queue.

How do I add human approval to an AI customer support agent?

Pushary gives an AI support agent one call that reaches a real person and waits for their answer. Wrap the refund, the account change, or the credit in decisions.ask(), and the agent holds until a human approves, denies, or types a replacement. If nobody answers inside your window it fails closed, so an unattended escalation never resolves itself into a payout.

Where it stops

The calls that should not be automatic

issueRefund(orderId, amount)
Money leaving the account is the one support action with no clean undo. A confidence score is not the same thing as a person agreeing to spend it.
applyAccountCredit(userId, amount)
Goodwill credits are where an agent that wants to please the customer gets expensive fastest, and where a policy written in a prompt drifts furthest from the one in the finance spreadsheet.
cancelSubscription(userId)
A cancellation the customer did not clearly ask for is a support failure that surfaces weeks later as a chargeback rather than a reply.
updateBillingEmail(userId, email)
Changing where invoices and password resets land is an account takeover vector, which is why it is usually the step a human is supposed to confirm.
escalate(ticketId, reason)
Handing off is only a handoff if somebody receives it. Most escalation paths assume a person is looking at a console at that moment.

The integration

Two calls inside the step you already have

Wrap the action, not the whole agent. Everything the agent does that is safe keeps running without asking anyone.

npm i @pushary/servertwo calls
import { createPusharyServer } from "@pushary/server"

const px = createPusharyServer({ apiKey: process.env.PUSHARY_API_KEY! })

async function resolveTicket(ticket: Ticket, proposed: RefundPlan) {
  // Small refunds inside policy do not need a person.
  if (proposed.amount <= POLICY.autoRefundCeiling) {
    return issueRefund(ticket.orderId, proposed.amount)
  }

  const decision = await px.decisions.ask({
    externalId: ticket.assignedAgentId,
    type: "confirm",
    question: `Refund $${proposed.amount} on order #${ticket.orderId}? ${proposed.reason}`,
  })

  if (!decision.approved) return replyWithAlternative(ticket)
  return issueRefund(ticket.orderId, proposed.amount)
}

Who answers

The approver is usually not an engineer

In support the approver is a team lead, a duty manager, or the account owner, and none of them live in your admin console. They are on a shift, between calls, or away from a desk entirely. That is why an approval built as an inbox message quietly becomes a six hour delay: the action is correctly blocked, it is just blocked for the rest of the afternoon. Pushary addresses the person by the id you already store for them and reaches the device they are actually carrying, so the decision comes back in the length of a conversation rather than the length of a shift.

Our agent already hands off to a human. Why add anything?

A handoff and a decision are different things. Handing off moves the ticket into a queue and ends the agent's run, so the customer waits for someone to pick it up and start again from the beginning. A decision keeps the run alive: the agent has already gathered the order, the history, and the proposed remedy, and it needs one person to say yes before it finishes the job it was doing. The second shape is faster for the customer and cheaper for you, because nobody re-reads the thread. It only works if the person can answer from wherever they are, and if the call fails closed when they cannot.

Worth reading: LangGraph on interrupts and resuming a paused run.

$99/ month, Partner plan

Embed it in your product. Your users never see a Pushary login or bill.

FAQ

Questions, answered

How do I add human approval to an AI customer support agent?
Pushary gives an AI support agent one call that reaches a real person and waits for their answer. Wrap the refund, the account change, or the credit in decisions.ask(), and the agent holds until a human approves, denies, or types a replacement. If nobody answers inside your window it fails closed, so an unattended escalation never resolves itself into a payout.
Can the approver send back a different amount instead of just yes or no?
Yes. Use the input type instead of confirm and the person types a value, which comes back as the answer on the decision. A common pattern in support is to ask for a confirm on anything inside policy and an input on anything above it, so the lead can counter with a smaller credit rather than rejecting outright and starting the thread over.
Does the customer see that a human was asked?
Only if you tell them. Pushary sends the question to your approver and returns the answer to your code. Nothing is shown to the end customer unless your agent decides to say something while it waits, which is usually the right call on anything that takes more than a few seconds.
What happens to the ticket if nobody answers?
The decision comes back denied after your window closes, and your agent decides what that means. Most support teams fall back to the existing queue at that point, so the worst case is the behaviour you have today rather than an unapproved refund. Because the wait is backed by a durable ledger, a deploy in the middle of the window does not lose the request.
Can different approvers handle different queues?
Yes. The externalId is your own id for whoever should decide, so you can route a billing question to the finance owner and a technical escalation to the on-shift lead by passing a different id. Each person connects their own phone once with a keyless link.

Put a person on the step that needs one.

Connect a user with one tap, ask a human with one call, and act on a fail-closed decision. Every decision is saved to a durable ledger you can audit.