Marble UI
← Components

Approval / Permission Request

Preview: Medium

Human-in-the-loop gate: the agent pauses until a person approves or denies a specific action.

Import

import { ApprovalCard } from '@marbleui/ui'

Props

<ApprovalCard>

PropTypeDefaultDescription
onApprove *required(opts: { remember: boolean; }) => void—
onDeny *required() => void—
risk *requiredRisk—
title *requiredReactNode—
allowRememberbooleantrueOffer "Always allow this action".
classNamestring—
descriptionReactNode—What exactly will happen.
details{ label: string; value: ReactNode; }[]—Structured details (recipient, amount, files…).
iconReactNode—

Usage notes

Human-in-the-loop gate: the agent waits for Approve or Deny on one specific action. To confirm an action the user started, use Popconfirm.

<ApprovalCard
  title="Send campaign email"
  risk="medium"
  details={[{ label: 'Recipients', value: '4,812 subscribers' }, { label: 'From', value: 'news@acme.com' }]}
  onApprove={({ remember }) => resolveApproval(call.id, { approved: true, remember })}
  onDeny={() => resolveApproval(call.id, { approved: false })}
/>
  • The card only reports the choice; resuming or skipping the run is up to you. After one press the buttons are replaced by a status line, focus moves to it, and the status region (there from the start) announces the decision. There’s no reset prop, so give each request its own key.
  • remember is true when “Always allow” was ticked; storing that rule is your job. The checkbox is hidden for risk="high" or allowRemember={false}.
  • Risk is spelled out in the badge text, not only color, and the card is a group labelled “Approval needed:” plus the title (pass a string title for that label).
Example source · show
import { ApprovalCard } from './approval-card'

const I = ({ d }: { d: string }) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden><path d={d} /></svg>
)

export const styles = {
  Medium: () => (
    <ApprovalCard
      className="w-[28rem]"
      icon={<I d="M4 4h16v16H4zM4 7l8 6 8-6" />}
      title="Send campaign email"
      description="October product update · Newsletter"
      risk="medium"
      details={[{ label: 'Recipients', value: '4,812 subscribers' }, { label: 'Send at', value: 'Now' }, { label: 'From', value: 'news@acme.com' }]}
      onApprove={() => {}}
      onDeny={() => {}}
    />
  ),
  High: () => (
    <ApprovalCard
      className="w-[28rem]"
      icon={<I d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6" />}
      title="Delete 1,204 inactive accounts"
      description="This can’t be undone."
      risk="high"
      details={[{ label: 'Criteria', value: 'No login in 24 months' }, { label: 'Backup', value: 'Snapshot taken 2 min ago' }]}
      onApprove={() => {}}
      onDeny={() => {}}
    />
  ),
}

Source: packages/ui/src/approval-card/approval-card.tsx

Catalog

Category
ai-agents
Priority
Pro
Primitive
composed