Skip to content
Twentyfirst UIHalo

Approval Card

AI · ApprovalCard

The human-in-the-loop decision: what an agent wants to do, the exact payload, and the four answers A–D.

Props: title (the action as one sentence), text (why), children (the payload, verbatim), agent, project, family ("money" | "infra" | "comms" | "marketing" | "apps" | "legal") or familyLabel, age, escalation, trace (node), note, heading, onDecide(key, text), decision ({key, by, at, text}: renders the one-line record), options (override A–D; each {key, label, input}), approveLabel (relabels A), compact. Legacy onApprove / onDeny / onEdit and state still work.

  • Always four answers in this order: A yes, as shown · B yes, with a change · C no, discard · D different instruction. B and D open a text field; ⌘/Ctrl+Enter sends.
  • A–D work as keys while the card has focus.
  • The payload is exactly what will run: the full mail, the amount and recipient, the command, the DNS change before and after. Never a summary.
  • Show the risk family, the age and the escalation time. Nothing runs automatically when time is up.
  • After a decision the card collapses to one line: letter, who, when (and the change or instruction).
  • All texts come from labels (see Labels); A–D wording can be changed per app.
Approval neededAtlas · SupportCommunication4 min

Send this email to ida@northwind.io

Atlas is set to drafts only, so it needs your OK.

Hi Ida, happy to add 40 seats before your renewal on 1 May. The quote is attached. Best, Mara
Escalates to the owner in 1:52 hOpen trace
C · No, discardRefund €10.00 to the card ending 4242Lukas · Yesterday 18:02
B · Yes, with a changeAdd TXT record ops-testMara Ellis · 08:10“Use TTL 300 instead”

Props

ApprovalCard

PropTypeNotes
titleReactNodeRequired
text?ReactNode
children?ReactNode
agent?string
project?string
family?RiskFamily
familyLabel?string
age?string
escalation?ReactNode
trace?ReactNode
note?ReactNode
heading?string
options?ApprovalOption[]
approveLabel?string
onDecide?(key: ApprovalKey, text?: string) => void
decision?ApprovalDecision | null
compact?boolean
onApprove?() => void
onDeny?() => void
onEdit?(text: string) => void
state?"approved" | "denied"
className?stringExtra class on the root element.
style?CSSPropertiesInline style on the root element.