Vercel AI SDK
Phone approvals for AI SDK agents. Your agent asks, your user taps Approve or Deny.
@pushary/ai-sdk plugs your users' phones into the AI SDK's own tool approval.
Before a tool runs, the AI SDK asks an approval function whether it may. Pushary is
that function: it sends the tool call to the right user's phone and returns their
answer. The tool runs only if they tap Approve.
It works with generateText, streamText and ToolLoopAgent in AI SDK 7.
Install
npm i @pushary/ai-sdk ai zod
export PUSHARY_API_KEY=pk_xxx.sk_xxxGet the key at Partner onboarding. The approval gate needs AI SDK 7 and Node.js 22 or later.
Connect a user once
import { enroll } from '@pushary/ai-sdk'
const { universalLink } = await enroll({ apiKey: process.env.PUSHARY_API_KEY! }, user.id)
// Show universalLink to this user. They open it on their phone once.user.id is your own id for the person who answers. Your users install the free
Pushary app. They never sign up or pay.
Require approval before a tool runs
Pass pusharyApproval() as toolApproval. List the tools that need a yes. Every
other tool runs as normal.
import { generateText, stepCountIs, tool } from 'ai'
import { z } from 'zod'
import { pusharyApproval } from '@pushary/ai-sdk'
const issueRefund = tool({
description: 'Refund an order',
inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
execute: async ({ orderId, amount }) => refundOrder(orderId, amount),
})
const lookupOrder = tool({
description: 'Look up an order',
inputSchema: z.object({ orderId: z.string() }),
execute: async ({ orderId }) => findOrder(orderId),
})
const { text } = await generateText({
model: 'openai/gpt-4o',
tools: { issueRefund, lookupOrder },
toolApproval: pusharyApproval({ externalId: user.id, tools: ['issueRefund'] }),
stopWhen: stepCountIs(5),
prompt: 'Refund order 1234 in full.',
})When the model calls issueRefund, the AI SDK stops before execute. Your user
gets a notification and taps Approve or Deny. On Approve the refund runs. On Deny,
or if nobody answers in time, the tool does not run and the model is told it was
not approved. The model cannot skip this, because the AI SDK asks before the tool
runs, not the model.
refundOrder and findOrder are your own functions.
Let the model ask a question
Sometimes the agent needs an answer, not a permission: which order, which address.
createPusharyTools gives the model an askHuman tool it can call.
import { generateText, stepCountIs } from 'ai'
import { createPusharyTools } from '@pushary/ai-sdk'
const { text } = await generateText({
model: 'openai/gpt-4o',
tools: createPusharyTools({ apiKey: process.env.PUSHARY_API_KEY!, externalId: user.id }),
stopWhen: stepCountIs(10),
prompt: 'Ask the customer which order they need help with.',
})The model chooses when to call askHuman, so use it for questions. Use
toolApproval above for anything that spends money, deletes data, or leaves your
system.
Good to know
- Each approval waits up to 55 seconds. For answers that may come later, see callbacks and resume.
- The same tool call never asks twice. A retry reuses the first question.
- Take
externalIdfrom your signed-in user, never from the model's tool input.
Source and more examples: @pushary/ai-sdk on GitHub. AI SDK reference: Tool calling and tool approval.