The problem
An AI agent with admin API access can make destructive, hard-to-reverse changes the moment it decides to. Deactivating the wrong user account, escalating permissions incorrectly, or deleting data silently can cause real harm before anyone notices.
// ❌ Direct admin call — executes immediately, no review, no record
await fetch(`https://api.yourapp.com/admin/users/${userId}/deactivate`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.ADMIN_API_KEY}` },
});
There’s no approval gate, no notification, and no audit trail showing who authorized the action.
The SafeFetch solution
Wrap the admin call in safeFetch() with approve: true and a callback URL. The action is held as awaiting_approval until a human approves it. Your backend is notified when the action reaches a terminal status.
Full example
import { safeFetch } from "safefetch";
const action = await safeFetch({
url: `https://api.yourapp.com/admin/users/${userId}/deactivate`,
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ADMIN_API_KEY}`,
"Content-Type": "application/json",
},
body: {
reason: "Flagged for policy violation",
initiated_by: "ai-agent",
},
approve: true,
// Email a signed magic-link approve/reject button to the reviewer
notify: { email: "security-team@yourcompany.com" },
// POST to your backend when the action completes or is rejected
callback: "https://yourapp.com/webhooks/safefetch",
// Prevent duplicate deactivation requests for the same user
dedupe: `deactivate-user:${userId}`,
});
console.log(action.id); // act_...
console.log(action.status); // "awaiting_approval"
The action is stored immediately and returns with status: "awaiting_approval". Your admin API is not called until a reviewer approves.
What happens next
Reviewer receives email
If you passed notify.email, SafeFetch sends an email with one-click Approve and Reject buttons. Links are signed — no login required.
Reviewer approves
Clicking Approve moves the action to pending and dispatches the request to your admin API. The receipt page shows the full details: who approved, when, and what your API returned.
Your callback fires
SafeFetch POSTs to your callback URL when the action reaches a terminal status (completed, failed, or cancelled). Check action.status and action.response_code to confirm the outcome and update your records.
Handling the callback
Verify the result in your webhook handler and take follow-up action:
import type { Action } from "safefetch";
// In your webhook handler (e.g., Express, Hono):
app.post("/webhooks/safefetch", async (req, res) => {
const action: Action = req.body;
if (action.status === "completed" && action.response_code === 200) {
// Admin action succeeded — log it and notify stakeholders
await db.auditLog.insert({
action_id: action.id,
type: "user_deactivated",
user_id: action.body?.userId,
completed_at: action.completed_at,
});
await notifyStakeholders(`User ${action.body?.userId} has been deactivated.`);
} else if (action.status === "cancelled") {
// Reviewer rejected — record the decision
await db.auditLog.insert({
action_id: action.id,
type: "deactivation_rejected",
user_id: action.body?.userId,
});
} else if (action.status === "failed") {
await alertTeam(`Admin action failed: ${action.last_error}`);
}
res.sendStatus(200);
});
Checking status programmatically
Poll or inspect the action at any point using its ID:
const action = await safeFetch.get("act_Xk9mP2nQ4rT1vW8sY");
if (action.status === "awaiting_approval") {
console.log("Pending reviewer sign-off.");
} else if (action.status === "completed") {
console.log("Action completed. API responded:", action.response_code);
} else if (action.status === "cancelled") {
console.log("Action was rejected by reviewer.");
} else if (action.status === "failed") {
console.log("Action failed:", action.last_error);
}
Approving or rejecting via API
If you have your own internal approval UI, skip the magic-link email and handle approval programmatically:
// Approve
await safeFetch.approve("act_Xk9mP2nQ4rT1vW8sY");
// Reject (cancel)
await safeFetch.cancel("act_Xk9mP2nQ4rT1vW8sY");
Receipt page as audit trail
Every action has a receipt page in the dashboard. It shows the full audit trail: who approved it, when, what your API returned, and how long delivery took. Share the URL with your security or compliance team.
https://api.safefetch.dev/dashboard/actions/act_Xk9mP2nQ4rT1vW8sY
For sensitive operations, store the action ID in your own database alongside the event. You can always retrieve the full record via safeFetch.get(action.id) or the API.
approve: true cannot be combined with sync: true. Synchronous mode waits for immediate completion — that’s incompatible with an asynchronous human decision.