Human-in-the-loop for CrewAI
Human-in-the-loop for CrewAI agents, on a phone.
Swap CrewAI's console human_input for a phone approval that blocks fail-closed until a real person answers.
The integration
Add it to your CrewAI agent
Run pip install pushary-crewai and give your agent make_ask_human_tool(user_id). It is a ready-made CrewAI tool that replaces the console human_input prompt. When the agent reaches an approval, it blocks fail-closed until a real person answers on their phone, then hands the agent the decision. Connect each end-user's phone once with a one-tap link keyed to your own id. The id is bound when you build the tool, so a prompt-injected agent can never ask the wrong person.
from crewai import Agent, Task, Crew
from pushary_crewai import connect, make_ask_human_tool
# Connect the user's phone once, keyed to your own id.
connect("user_123")
agent = Agent(
role="Ops",
goal="Ship safely",
backstory="Careful operator.",
tools=[make_ask_human_tool("user_123")], # blocks on a real person, fail-closed
)Why you need it
CrewAI pauses the agent. Pushary reaches the human.
CrewAI's built-in human_input=True only prompts on stdin, so nothing happens unless someone is watching the terminal. That is fine while you are developing a crew and useless the moment it runs on a schedule or on a server. pushary-crewai moves that approval to the person's phone. make_ask_human_tool gives the agent a tool that blocks fail-closed until they answer, delivers to the device they carry, and logs every answer. Each of your end-users approves their own decision rather than everything routing to whoever launched the crew.
What CrewAI already gives you
human_input=True on a task
CrewAI ships a human review step. Set human_input=True on a task and the crew stops after the agent produces its output, prints it, and waits for someone to type feedback on stdin. It is genuinely useful while you are developing, because you see the draft and can push back on it before the next agent picks it up.
task = Task(
description="Draft the reply to the customer",
agent=support_agent,
human_input=True, # blocks on input() at the terminal
)Where it stops
- It reads from stdin. In a container, a cron job, or a queue worker there is no stdin, so it either hangs forever or throws.
- It stops the whole crew, not just the one task, and every other agent sits idle behind it.
- The feedback is a string that goes back into the model. Nothing records that a person reviewed it.
Before you ship it
Things that bite on CrewAI
- It works locally and hangs in production
- This is the single most common CrewAI support thread. human_input=True is fine on a laptop and becomes an invisible hang the first time the crew runs somewhere without a terminal attached. If your crew stopped producing output after you deployed it, this is usually why.
- Kickoff for many inputs serialises on the human
- kickoff_for_each with a human_input task asks about every single item, one after another, at the terminal. Ten inputs means ten prompts nobody is there to answer.
- Review is not the same as approval
- human_input gives the agent feedback text to incorporate. It does not stop the task from completing. If you need the action blocked unless a person says yes, feedback is the wrong shape and you want a decision with a fail-closed default.
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.
- 01
Install the adapter
Run pip install pushary-crewai and set your Pushary API key in the environment.
- 02
Connect the phone
Call connect(external_id) once per end-user. It returns a link the person taps once to turn on approvals. It opens the Pushary app, which is what carries Approve and Deny on the lock screen, and they need no account of ours.
- 03
Give the agent the tool
Pass make_ask_human_tool(user_id) in the agent's tools list and drop human_input=True. The user id is bound to the tool, so the model cannot change who gets asked.
- 04
It stays fail-closed
The tool treats anything short of an explicit approval as a stop. A decline, a timeout, or no answer all mean the agent does not act.
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.
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
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: 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
Does CrewAI have built-in human-in-the-loop?
Is there a CrewAI package for Pushary?
How do I make a CrewAI approval fail-closed?
Can each of my end-users get approvals on their own phone?
What can a human answer beyond yes and no?
Why does human_input hang when my crew runs in production?
Let your CrewAI 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.
Partner plan, $99 a month. Your users never see a Pushary login or bill.