Build approvals into your agent
Add human in the loop approvals to the AI agent in your product. Your user gets a push notification, taps Approve or Deny, and your code gets the answer.
Pushary adds human approval to the agent inside your own product. Before a risky step, such as a refund, a customer email or a delete, your backend asks one of your users. They get a push notification with your product's name, tap Approve or Deny, and your code carries on. If nobody answers, the action does not run.
npm i @pushary/serverWant to see it work first? Try it in your browser. No signup.
What you need
- A Partner plan and its API key. Get both at Partner onboarding.
- Your own id for each user, such as
user.id. Pushary calls itexternalId. - Your users install the free Pushary app and open your link once. They never sign up or pay.
Ask your first question
Install the SDK and set your key
npm i @pushary/server
export PUSHARY_API_KEY=pk_xxx.xxxKeep the key on your server. Never put it in browser code or a prompt.
Connect a user once
import { createPusharyServer } from '@pushary/server'
const pushary = createPusharyServer({ apiKey: process.env.PUSHARY_API_KEY! })
const { universalLink } = await pushary.enroll(user.id)
// Show universalLink to this user as a button or a QR code.The user opens the link on their phone. It opens the Pushary app if they have it. If not, the page offers the app or browser notifications. After that, every question for this externalId reaches their phone.
The link can be redeemed once per channel (the phone app and browser push) and expires after 24 hours. Save the fact that the user connected, not the link.
Ask before the risky step
const decision = await pushary.decisions.ask({
externalId: user.id,
question: 'Refund $480 to order #4471?',
})
if (decision.approved) {
await issueRefund()
}Your user's phone buzzes with the question. When they tap Approve, approved is true and the refund runs. issueRefund is your own function.
What happens when nobody answers
ask waits up to 55 seconds for an answer. approved is true only when the person tapped Approve. Deny, an expired question and no answer at all give approved: false, so the refund does not run.
The question stays open after ask returns, for 1 hour by default. To act on a late answer, save decision.decisionId and read it again later. The full Partner guide shows how.
What your user sees
- A notification titled with your product name, such as Acme needs your approval.
- They tap Approve or Deny from the lock screen.
- Your code gets the answer and carries on.
Use your framework's approval hook
Most agent frameworks can stop before a tool runs. These adapters put your user's phone behind that stop.
Vercel AI SDK
pusharyApproval() as the AI SDK's toolApproval.
LangGraph
An approval node in front of the risky node, in TypeScript or Python.
OpenAI Agents SDK
Answers needsApproval interruptions from your user's phone.
Mastra
Approves or declines tools marked requireApproval.
Claude Agent SDK
A canUseTool that asks your user before a tool runs.
Every other adapter
Eve, CrewAI, Pydantic AI, durable workflows and your own framework.
Next steps
Read the full Partner guide
Callbacks, resuming later, bound keys and answers from your own app.
Test without a phone
A sandbox key settles each question as approve, deny or no answer.