Human-in-the-loop for Mastra

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

Call decisions.ask() from a Mastra tool's execute for a blocking approval, or from a workflow step that suspends and resumes when the wait is long. Either way Pushary sends the question to a person's phone, holds the agent on a durable wait, and returns a fail-closed decision the step can act on. Install @pushary/server, connect each end-user's phone once with a keyless one-tap link, and the externalId is your own id for that user. There is no @pushary/mastra package and you do not need one.

npm i @pushary/servertwo calls
import { createTool } from "@mastra/core/tools"
import { z } from "zod"
import { createPusharyServer } from "@pushary/server"

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

export const askHuman = createTool({
  id: "ask-human",
  description: "Ask a real person to approve before acting.",
  inputSchema: z.object({ question: z.string() }),
  outputSchema: z.object({ approved: z.boolean() }),
  execute: async ({ question }) => {
    const d = await pushary.decisions.ask({
      question,
      externalId: "user_123",
      type: "confirm",
    })
    return { approved: d.approved }
  },
})

Why you need it

Mastra pauses the agent. Pushary reaches the human.

Mastra can suspend a workflow step and persist the snapshot, and a suspended step does not tell anyone it is waiting. decisions.ask() completes the loop: the question lands on a person's phone, the answer comes back fail-closed, and the ledger keeps the record. For a wait measured in hours, create the decision with a callbackUrl inside the suspended step and resume the run from Pushary's signed webhook.

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 and set your key

    Run npm i @pushary/server and set PUSHARY_API_KEY.

  2. 02

    Connect the phone

    Call enroll(externalId) once per end-user to connect their phone with a keyless one-tap link.

  3. 03

    Wrap decisions.ask in a tool

    Define a createTool whose execute calls decisions.ask, and add it to your Agent({ tools }). The agent blocks on that step until the person decides.

  4. 04

    Go durable for long waits

    For long waits, suspend the workflow step after creating the decision with a callbackUrl, then resume the run from Pushary's signed webhook. Mastra persists the snapshot, so the wait holds no compute.

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.

For your users

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.

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, and every approval lands with the right person. No account, no app, no key on their side.

Durable, fail-closed waits

The agent stays open on a durable wait backed by a decisions ledger. If nobody answers in the window you set, the decision comes back denied, so a timeout or a restart never turns into an unapproved action.

Phone-first delivery

Pushary reaches the person on the device they carry: native app push first, 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

Human-in-the-loop for other frameworks

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. The durable store and the audit log come with it.

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