Impraint

Approvals

Hold a risky action until a person approves it.

When a rule's action is human_approval, Guard holds the agent's action until an admin approves or rejects it in Slack or the dashboard. If anything goes wrong or nobody answers, the action is blocked.

Approvals are part of Rules and circuit breaker. Add a rule with the action human_approval, for example action_amount greater than 5000, and Guard does the rest.

How it works

  1. Your agent asks Guard "may I do this?" (@guard.tool or guard.intercept).
  2. A human_approval rule matches, so Guard creates a pending approval and tells the SDK to wait.
  3. Guard notifies your organisation in Slack, with Approve and Reject buttons. If Slack is not set up, or still fails after a few retries, it emails your alert recipients instead. During beta, Impraint connects Slack for you when your organisation is set up.
  4. An admin decides in Slack or in the dashboard (Govern → Approvals), with an optional reason (up to 500 characters).
  5. The SDK sees the decision. Approved lets the action run. Rejected, timed out, or any doubt blocks it.
@guard.tool(amount="refund_amount")
def process_refund(order_id: str, refund_amount: float):
    ...  # runs only if the rules allow it, or an admin approves

try:
    process_refund("A-100", 7500)
except ImpraintGuardApprovalRejected:
    ...  # rejected or timed out

Without @guard.tool, wait for the answer yourself:

decision = guard.intercept("process_refund", {"action_amount": amount})
if decision.needs_approval:
    approved = guard.wait_for_approval(decision.approval_id, decision.timeout_seconds)
    if not approved:
        return {"error": "Rejected or timed out."}

In async code use intercept_async and wait_for_approval_async.

Statuses

StatusMeaning
pendingWaiting for a person
approvedAn admin approved in time
rejectedAn admin rejected in time
timed_outNobody decided in time, or the decision came too late

Only pending can change, and once decided an approval is final. A click that arrives after the deadline is stored as timed_out, never approved.

Timeout

The agent waits 300 seconds by default. Set a different time on the rule with approval_timeout_seconds. When time runs out the approval becomes timed_out and the action is blocked. Guard checks for overdue approvals every minute.

Who can decide

  • Only an admin of your organisation can approve or reject.
  • To decide from Slack, an admin first links their own Slack member ID. In Slack, open your profile, click the three dots, and choose Copy member ID. It looks like U01ABC234DE. Then, in the dashboard, go to Settings → Approve from Slack, paste the ID and click Save. Clear the field and save to remove the link.
  • A Slack click from someone who is not linked, or not an active admin, is refused, and that person gets a private note in Slack.
  • A Slack ID can belong to one member of an organisation.
  • Admins can also decide in the dashboard under Govern → Approvals, with the rule, metric, value and limit shown so they see why it was flagged.

Where notifications go

Approvals use the same channels as Alerts: your Slack connection and email recipients, set under Settings → Alert channels. If neither is configured, the approval still exists and can be decided in the dashboard, but nobody is notified.

Guard tries Slack first. If the send fails, it retries up to three times, after 5, 15 and 30 seconds, while there is still time before the approval times out. Email is sent only when Slack is not set up, or is still failing after the retries. It goes to all your alert recipients.

The email names the agent and the action, gives the reason (with personal data masked) and says how much time is left. It never contains the action's parameters, and it has no approve link, so the approver opens Govern → Approvals to decide.

Slack buttons need the connection Impraint sets up

The Approve and Reject buttons work through the Slack connection Impraint sets up for your organisation during beta. If you paste a webhook from a Slack app of your own, the message can arrive, but the buttons will not work. Decide in the dashboard instead.

After a decision, the Slack message is replaced with "Approved by …", "Rejected by …" or "Timed out", and the buttons are removed.

Protections

  • No duplicates. An identical pending request from the same agent reuses the existing approval instead of creating another.
  • Flood control. An agent can have at most 10 pending approvals, and an organisation at most 50. Past that, new requests are blocked at once with rule approval_flood, and your team gets a notice, at most once a minute for each agent. This protects people from approval fatigue.
  • Masked values. Personal data in the action's parameters is masked in Slack, email and reasons. Keys named like password, token, api_key or authorization always show [hidden]. Viewers in the dashboard also see masked values, and the page tells them some values are hidden for their role.
  • Audit trail. Requests, notifications, decisions, refused clicks, time-outs and flood blocks are written to the tamper-evident audit log.

Use fail_open=False for risky actions

Once an approval exists, every failure ends in "blocked". But if Guard's rule check itself times out or cannot be reached, the fail_open setting decides, and the default (True) lets the action through with no approval. For agents that handle risky actions, set fail_open=False.

Known limits in beta

  • Any linked admin can decide, including the admin whose agent made the request.
  • Slack ID ownership is not proven, only linked by an admin and audited.
  • Slack buttons work only through the connection Impraint sets up. Customer-owned Slack apps are not supported yet.
  • One admin decides. There is no second approver, quorum, reminder or escalation yet.
  • The 10 and 50 caps and the fallback email recipients (all alert recipients) are not configurable.
  • A timeout always means reject. You cannot choose auto-approve.
  • You cannot edit the action's parameters before approving.

On this page