Comparison
A HumanLayer alternative for agent approvals
Nov 4, 2026 · 5 min read
The short version
HumanLayer started as an SDK that let agents ask a person before acting:
require_approval to gate a function, human_as_tool to ask
a question. Today HumanLayer builds a coding-agent IDE, and the approvals SDK hasn’t
had a release since June 2025. If you still depend on it, Run it by covers the same
two patterns, and the person who answers needs nothing but a link.
Checked against HumanLayer’s own pages in November 2026. We found no official deprecation notice, so check with HumanLayer before you plan around our reading.
What changed at HumanLayer
- humanlayer.dev now describes a multiplayer coding agent IDE and cloud for running Claude Code, Codex and other coding agents.
- The last release of the humanlayer package on PyPI is 0.7.9, from June 2025.
- The approvals documentation is no longer on the site, and the repository now holds the new product.
That’s a sensible move for HumanLayer, and if you supervise coding agents as a team, their new product is built for that. It just leaves agents that need a person’s sign-off without the SDK they used.
The same patterns in Run it by
-
Gate an action (
require_approval): create a request withmode: "approval", the reason and the people who decide. Act only when the verdict isapproved. -
Ask a question (
human_as_tool): create a request withmode: "form"and a JSON Schema for the answer. It comes back validated against that schema. -
Hear back: a signed webhook,
getStatuswith a wait of up to 25 seconds, or, over MCP on Builder and Pro, a notification the moment someone answers.
Example
A small require_approval in Python over the REST API. It waits in the
call; in production, prefer the webhook.
import os, requests
API = "https://runitby.app/v1/requests"
AUTH = {"Authorization": f"Bearer {os.environ['RUNITBY_API_KEY']}"}
def require_approval(reason, approvers):
request = requests.post(API, headers=AUTH, json={
"mode": "approval",
"title": reason,
"requester": "The billing agent",
"audience": {"kind": "named", "recipients": approvers},
"approval": {"reason": reason, "policy": "any", "verify": "email"},
"expiresIn": "24h",
"sendEmail": True,
}).json()
while True:
status = requests.get(
f"{API}/{request['requestId']}?wait=25", headers=AUTH
).json()
if status["verdict"]:
return status["verdict"] == "approved"
if require_approval("Refund order #4821, $1,200",
[{"name": "Emily Lee", "email": "emily@acme.com"}]):
issue_refund("4821")
declined and expired both return False, so
silence never turns into a refund.
What’s different
- Approvers don’t need Slack or an account. Each named person gets their own link by email, or you pass the link on yourself, including into Slack.
-
Several approvers with a policy (any, all, majority or a number), an optional
email code per person, and an expiry that ends as
expired. - There’s no decorator to wrap your functions; you call the API or the MCP tools. If you relied on approvals arriving as Slack messages with buttons, Run it by doesn’t do that.
For a wider look at the options, see Run it by vs approval tools.
Try it with your agent
Free for 250 requests a month, no card. Add the MCP server from Claude Code or Codex with one command, or use the REST API.