Human-in-the-loop for Mastra

Human-in-the-loop for Mastra agents and workflows.

Ask customers on their phone, with Mastra enforcing which actions require approval.

The integration

Add it to your Mastra agent

Pushary brings confirm, select and input questions to an enrolled customer in the native app. createPusharyAskTool collects an optional answer during a bounded request; it does not gate other tools. Use Mastra's native approval boundary for protected actions and its configured persistent storage for delayed reviews. Your backend validates the recorded response and resumes the intended run. Updated agent approval and deferred-review helpers are awaiting a forthcoming package release.

bash
npm i @pushary/mastra
optional customer question
import { createPusharyAskTool, connect } from "@pushary/mastra"

await connect({ apiKey: process.env.PUSHARY_API_KEY! }, "user_123")

export const askHuman = createPusharyAskTool(
  { apiKey: process.env.PUSHARY_API_KEY! },
  { externalId: "user_123" },
)

Why you need it

Mastra pauses the agent. Pushary reaches the human.

Mastra owns execution and persisted run state. Pushary reaches the customer and records their answer. Your application owns the decision-to-run mapping, response validation, resume trigger and recovery after an uncertain action. A webhook is a notification to your application, not automatic successful resumption. Follow the tested agent and workflow examples in the package README for the exact installed release and confirm its persistent storage requirements.

What Mastra already gives you

suspend() and run.resume()

A workflow step can suspend, and application code can later call run.resume(). Recovery across processes requires configured persistent Mastra storage and saved run identity. Validate resumeData against the expected review and authorized customer before using it to permit an action.

native
const approval = createStep({
  id: "approval",
  execute: async ({ suspend, resumeData }) => {
    if (!resumeData) return await suspend({})
    return { approved: resumeData.approved }
  },
})

Where it stops

  • A suspended step is silent. Storage knows the run is waiting and no human does.
  • run.resume() has to be called by something. You build the route that receives the answer and maps it back to the right runId.
  • The snapshot holds workflow state, not an audit record of who approved and what they were shown.

Before you ship it

Things that bite on Mastra

You need a correlationId to runId map
The answer arrives on a webhook that knows about a decision, not about your workflow. Store the mapping when you suspend, or you will have an answer and no idea which suspended run it belongs to. This is the piece people forget until the first real approval comes back.
resumeData is undefined on the first pass
The step executes once to suspend and again to resume, through the same execute function. Forgetting the undefined branch on the first pass throws before it ever suspends.
Agent tools and workflow steps are different tools for different waits
The blocking ask tool suits a short question. Delayed agent reviews and workflow suspensions need persisted Mastra state plus application-owned response handling. An optional question is not enforcement, regardless of how long it waits.

How it works

From ask to resume

Connect the phone once, then ask a human from the tool or step that guards the action. The waiting, the delivery, and the record are handled for you.

  1. 01

    Install the adapter

    Run npm i @pushary/mastra and set your Pushary API key in the environment.

  2. 02

    Connect the phone

    Create an enrollment link for the authorized customer and have them complete it in the native app. Confirm can offer notification actions; select and input open the app. Bind externalId from trusted application context.

  3. 03

    Add the tool or the step

    Add createPusharyAskTool for optional questions. Gate protected actions through Mastra's approval boundary. For deferred work, use the package reference matching your installed version and configure persistent storage.

  4. 04

    Resume on the answer

    Your backend retrieves and validates the recorded answer, then resumes the intended Mastra run. Keep a protected action blocked without its required approval. Preserve select/input as data and handle timeout, denial and uncertain continuation separately.

A decision is a yes or no confirm, a multiple-choice select, or a free-text input. Every one is stored with its outcome and kept in the audit log.

The Partner plan

Your agent asks your users, not just you

Ship the agent to real users and let each of them approve their own decisions from their phone, under your brand.

$99/ month, every framework included
Start with Pushary

Your users never see a Pushary login or bill.

Your users answer on their own phones

The externalId is your own id for the end-user. Connect their phone once with a keyless one-tap link that opens the Pushary app, and every approval lands with the right person. No account and no key on their side.

Durable, fail-closed waits

Pushary stores the decision; your application persists its own paused workflow and schedules resumption. Require an explicit, verified approval before acting. Pending, expired and cancelled requests are not approval.

Phone-first delivery

Pushary reaches the person on the device they carry: the app first, because it is the only channel that puts Approve and Deny on a lock screen, then PWA push notifications, then Slack if you route it there. You do not build a delivery pipeline.

Signed webhooks and an audit trail

Every decision is written to a durable ledger you can query and export, and resolved decisions post a signed webhook to your callback URL. You have the record of who approved what and when.

FAQ

Questions, answered

Is there a Mastra package for Pushary?
Yes. @pushary/mastra is published. Its package README is the version-specific reference. Updated enforced agent approvals, deferred questions and expanded workflow outcomes are awaiting release alongside @pushary/server 2.1; do not assume the current npm version includes those source changes.
Should I use the tool or the workflow step?
Use the ask tool for a short optional question. Use a persisted agent-review or workflow recipe for a longer wait, following the installed package version. Your application must route the validated response to the saved run and handle continuation failure.
What happens when nobody answers?
Silence supplies no human approval. Keep protected actions blocked and record an unanswered or expired request separately from a customer rejection. A decision record alone cannot recover a Mastra run.
Can each of my end-users approve on their own phone?
Yes. Resolve the authorized reviewer in your application and enroll that customer in the native Pushary app. They do not need their own API key or paid plan. The person requesting an action is not necessarily authorized to approve it.
Does the wait survive a restart or a deploy?
A blocking ask does not survive process loss. A suspended run can be recovered with persistent Mastra storage, saved correlation and application-owned resume handling. Test restart and duplicate handling with your deployed storage; a saved answer does not guarantee a completed business action.
When should I use suspend() instead of a blocking ask tool?
Use a persisted suspension when the customer may answer after the request or worker exits. Configure Mastra storage and retain the run identity before waiting. Your application must validate the answer and trigger continuation; a callback URL alone does not supply that recovery.

Let your Mastra agent ask a human.

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.

Start with Pushary

Partner plan, $99 a month. Your users never see a Pushary login or bill.