Autonomous agents are great at doing, and terrible at pausing. That is the whole problem with letting one run unattended: the moment it wants to spend money, publish something, or delete data, you want a human gate — not a silent `continue`. The Agent Media Tools approvals API gives you exactly that: a durable human-in-the-loop checkpoint your agent can create with one HTTP call, wait on, and react to when a person says yes or no.
This post walks the full flow with real curl and Python requests examples, then shows the same workflow through MCP tools. Everything here is backed by live routes on agentmediatools.com, so you can copy the snippets and run them with your own agent key.
An approval request moves through four states: pending → approved, denied, or expired. The lifecycle is simple enough to fit in a diagram:
1. Your agent POSTs an approval request with a summary, optional action label, optional amount in cents, and optional structured details.
2. The API stores it, returns an approve_url, and notifies the human through any configured channels (email, Discord webhook, Telegram).
3. The human opens the link and approves or denies (optionally with a note), or your own tooling POSTs the decision programmatically.
4. Your agent polls until the status is terminal, then branches on the result.
Pending requests auto-expire after the TTL you set (default 48 hours), and every request and decision is written to a signed receipt for your audit trail. No polling loop needs to run forever — expiry is handled server-side.
You authenticate with your agent API key (an mt_-prefixed bearer token). The endpoint is POST /api/agent/approvals. Only summary is required; everything else is optional.
curl -sS https://agentmediatools.com/api/agent/approvals \
-H "Authorization: Bearer $AMT_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "purchase",
"summary": "Approve $25 credit pack before continuing batch job",
"amount_cents": 2500,
"details": {"job": "batch-4821", "items": 120},
"ttl_hours": 24
}'
You get back a 201 with the approval object, the human-facing link, and which channels were notified:
{
"success": true,
"approval": {
"id": "apr_3f8a...",
"status": "pending",
"approve_url": "https://agentmediatools.com/approve/9kL2mN...",
"expires_at": "2026-08-04 12:00:00"
},
"notified": {"email": true, "discord": true, "telegram": false},
"message": "Human must open approve_url (email/Discord/Telegram if configured). Agent polls poll_url until status is approved|denied|expired."
}
Store the approval id — that is what you poll with. The approve_url contains a one-time token; it is the only thing the human needs.
Two paths lead to a decision:
Send the human the approve_url. The page shows the action, summary, amount, details, and expiry, with Approve and Deny buttons plus an optional note field. No login required; the token in the URL is the credential. This is what email/Discord/Telegram notifications point to.
If your human approves inside your own dashboard or Slack bot, POST the decision to /api/agent/approvals/decide/:token:
curl -sS -X POST https://agentmediatools.com/api/agent/approvals/decide/9kL2mN... \
-H "Content-Type: application/json" \
-d '{"decision": "approved", "note": "Looks good, ship it"}'
Decisions after the fact return 400 with the current status, and unknown tokens return 404, so double-deciding is impossible.
Here is a complete Python loop using only requests. It creates the approval, polls every 10 seconds, and returns the terminal status plus the human's note:
import os, time, requests
AMT_KEY = os.environ["AMT_KEY"]
BASE = "https://agentmediatools.com"
headers = {"Authorization": f"Bearer {AMT_KEY}"}
# 1. Create the checkpoint
r = requests.post(f"{BASE}/api/agent/approvals", headers=headers, json={
"action": "deploy",
"summary": "Deploy v2.4.1 to production",
"details": {"env": "prod", "sha": "a1b2c3d"},
"ttl_hours": 2,
})
r.raise_for_status()
approval = r.json()["approval"]
print("Human, please review:", approval["approve_url"])
# 2. Poll until terminal
while True:
status = requests.get(
f"{BASE}/api/agent/approvals/{approval['id']}", headers=headers
).json()["approval"]["status"]
if status in ("approved", "denied", "expired"):
break
time.sleep(10)
# 3. Branch on the result
print("Final status:", status)
if status == "approved":
print("Proceeding with deploy")
elif status == "denied":
print("Deploy blocked by human")
else:
print("Request expired — re-request or skip")
The poll endpoint also auto-expires any pending request whose TTL has passed, so a stale checkpoint never blocks you forever.
If your agent talks MCP (Claude, or any MCP client), the hosted server exposes the same flow as request_approval and get_approval tools. Set your agent key in the environment and the tool calls map 1:1 to the REST endpoints above — request_approval POSTs to /api/agent/approvals with action, summary, amount_cents, details, and ttl_hours; get_approval polls /api/agent/approvals/:id. The agent should call request_approval, hand the returned URL to the human, and loop on get_approval until the status leaves pending.
Approvals notify the channels you configure on your key via POST /api/agent/keys/:id/notify-channels (authenticated as the account owner). You can set email, discord_webhook, or telegram_bot_token + telegram_chat_id in one call. Discord webhook URLs are validated, Telegram tokens are validated against the standard bot-id:token shape, and secrets are stored masked server-side — the response never returns full tokens. Clear a channel by passing null or an empty string.
curl -sS -X POST https://agentmediatools.com/api/agent/keys/123/notify-channels \
-H "Content-Type: application/json" \
-d '{
"email": "ops@example.com",
"discord_webhook": "https://discord.com/api/webhooks/123/token"
}'
From then on, every approval request fans out to those channels with the approve_url attached — your human clicks, decides, and your agent keeps moving.
amount_cents so the human sees a dollar figure on the approve page.Because every create and every decision writes a signed receipt (kind approval), you get a tamper-evident record of what was requested, who decided, and the attached note — useful for compliance and for explaining agent behavior after the fact.
ttl_hours to something shorter than your job timeout so a parked approval never blocks the queue.expired as a first-class outcome in your agent's branching — re-request with a new summary or skip.details; it renders as JSON on the approve page.Human-in-the-loop doesn't mean slow — it means the machine waits exactly as long as the human needs, and never longer. Create an approval request from your own agent today.
Run the same tools in the browser, or call them from your agent with an API key.
Open toolbox