✨ From vibe coding to vibe deployment. UBOS MCP turns ideas into infra with one message.

Learn more
Andrii Bidochko
  • Updated: March 23, 2026
  • 8 min read

Building a Human‑Approval Dashboard for OpenClaw Remediation Agents

A human‑approval dashboard for OpenClaw remediation agents is a web UI that lets operators review, approve, or reject remediation actions triggered by OpenClaw, while automatically syncing status updates through Moltbook notifications.

1. Introduction

Security‑oriented developers often automate threat containment with OpenClaw remediation agents. Automation speeds up response, but blind execution can cause unintended service disruption. A human‑approval dashboard adds a safety net: every remediation request is queued, displayed, and requires explicit operator consent before the agent proceeds. This guide walks you through a complete UI implementation on the UBOS homepage, covering layout, state management, approval workflow, API integration, and the final step of connecting to Moltbook notifications.

2. Overview of OpenClaw Remediation Agents

OpenClaw is an open‑source framework that detects security incidents and launches remediation agents—small services that can isolate containers, rotate credentials, or patch vulnerable code. Each agent exposes a minimal REST API:

  • POST /agents/{id}/execute – start remediation
  • GET /agents/{id}/status – fetch current state
  • POST /agents/{id}/cancel – abort an in‑progress action

When an agent is triggered, OpenClaw emits a JSON payload to a configurable webhook. Our dashboard will consume this payload, present it to a human, and only forward the execute call after approval.

3. UI Design Overview

3.1 Layout and Components

We’ll build the UI with React and Tailwind CSS. The dashboard consists of three primary components:

  1. Header – branding, navigation, and a status indicator.
  2. Request Queue – a table that lists pending remediation requests.
  3. Approval Modal – a dialog that shows request details and provides Approve/Reject buttons.

{/* Header component */}
function Header() {
  return (
    <header className="flex items-center justify-between bg-white shadow p-4">
      <h1 className="text-xl font-bold">OpenClaw Human‑Approval Dashboard</h1>
      <span className="text-green-600">Connected to Moltbook</span>
    </header>
  );
}

3.2 State Management

We’ll use Redux Toolkit for predictable state handling. The store holds:

  • requests – an array of pending remediation objects.
  • selectedRequest – the request currently displayed in the modal.
  • notificationStatus – success/error flags from Moltbook webhook calls.

import { createSlice } from '@reduxjs/toolkit';

const remediationSlice = createSlice({
  name: 'remediation',
  initialState: {
    requests: [],
    selectedRequest: null,
    notificationStatus: null,
  },
  reducers: {
    addRequest(state, action) {
      state.requests.push(action.payload);
    },
    selectRequest(state, action) {
      state.selectedRequest = action.payload;
    },
    clearSelection(state) {
      state.selectedRequest = null;
    },
    setNotificationStatus(state, action) {
      state.notificationStatus = action.payload;
    },
  },
});

export const {
  addRequest,
  selectRequest,
  clearSelection,
  setNotificationStatus,
} = remediationSlice.actions;
export default remediationSlice.reducer;

4. Approval Workflow

4.1 Request Queue

The queue is a responsive table built with Tailwind utilities. Each row displays the agent name, severity, timestamp, and a Review button.


function RequestQueue() {
  const dispatch = useDispatch();
  const requests = useSelector(state => state.remediation.requests);

  return (
    <table className="min-w-full divide-y divide-gray-200">
      <thead className="bg-gray-50">
        <tr>
          <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Agent</th>
          <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Severity</th>
          <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Received</th>
          <th className="px-6 py-3"><span className="sr-only">Review</span></th>
        </tr>
      </thead>
      <tbody className="bg-white divide-y divide-gray-200">
        {requests.map(req => (
          <tr key={req.id}>
            <td className="px-6 py-4 whitespace-nowrap">{req.agentName}</td>
            <td className="px-6 py-4 whitespace-nowrap">
              <span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${req.severity === 'high' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800'}`}>
                {req.severity}
              </span>
            </td>
            <td className="px-6 py-4 whitespace-nowrap">{new Date(req.timestamp).toLocaleString()}</td>
            <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
              <button
                onClick={() => dispatch(selectRequest(req))}
                className="text-indigo-600 hover:text-indigo-900">
                Review
              </button>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

4.2 Human Approval UI

The modal pulls selectedRequest from the store and displays a JSON payload, a risk summary, and two action buttons.


function ApprovalModal() {
  const dispatch = useDispatch();
  const request = useSelector(state => state.remediation.selectedRequest);

  if (!request) return null;

  const handleApprove = async () => {
    await approveRequest(request.id);
    dispatch(clearSelection());
  };

  const handleReject = async () => {
    await rejectRequest(request.id);
    dispatch(clearSelection());
  };

  return (
    <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
      <div className="bg-white rounded-lg overflow-hidden shadow-xl max-w-lg w-full">
        <div className="p-6">
          <h2 className="text-xl font-bold mb-4">Review Remediation Request</h2>
          <pre className="bg-gray-50 p-3 rounded mb-4 overflow-x-auto">{JSON.stringify(request.payload, null, 2)}</pre>
          <div className="flex justify-end space-x-3">
            <button onClick={handleReject} className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">Reject</button>
            <button onClick={handleApprove} className="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700">Approve</button>
          </div>
        </div>
      </div>
    </div>
  );
}

4.3 Status Updates

After an operator clicks Approve, the dashboard calls the OpenClaw /execute endpoint, then posts a status message to Moltbook. The UI reflects three states:

  • Pending – request sits in the queue.
  • In‑Progress – API call succeeded, waiting for agent feedback.
  • Completed / Failed – final status received via Moltbook webhook.

5. API Integration

5.1 Endpoints for Agent Actions

All API calls are wrapped in a tiny api.js helper that injects the UBOS platform overview authentication token.


import axios from 'axios';

const API_BASE = 'https://api.openclaw.io/v1';
const TOKEN = process.env.REACT_APP_UBOS_TOKEN; // fetched from UBOS secrets

const api = axios.create({
  baseURL: API_BASE,
  headers: { Authorization: `Bearer ${TOKEN}` },
});

export const approveRequest = async (requestId) => {
  const resp = await api.post(`/agents/${requestId}/execute`);
  return resp.data;
};

export const rejectRequest = async (requestId) => {
  const resp = await api.post(`/agents/${requestId}/cancel`);
  return resp.data;
};

export const fetchStatus = async (requestId) => {
  const resp = await api.get(`/agents/${requestId}/status`);
  return resp.data;
};

5.2 Authentication and Error Handling

UBOS provides a built‑in UBOS partner program that issues short‑lived JWTs. The helper above reads the token from an environment variable, ensuring no secret is hard‑coded.

Each request is wrapped in a try / catch block. On error we dispatch setNotificationStatus to surface a toast using the Workflow automation studio component.


export const safeApprove = async (id, dispatch) => {
  try {
    await approveRequest(id);
    dispatch(setNotificationStatus({ type: 'success', message: 'Remediation started' }));
  } catch (e) {
    dispatch(setNotificationStatus({ type: 'error', message: e.message }));
  }
};

6. Connecting to Moltbook Notifications

6.1 Webhook Setup

Moltbook expects a POST request with a JSON body containing requestId, status, and an optional details field. In UBOS we create a Web app editor on UBOS endpoint that registers itself as a webhook URL.


import express from 'express';
import bodyParser from 'body-parser';
import { updateRequestStatus } from './store';

const app = express();
app.use(bodyParser.json());

app.post('/moltbook/webhook', (req, res) => {
  const { requestId, status, details } = req.body;
  updateRequestStatus(requestId, status, details);
  res.status(200).send('OK');
});

app.listen(3000, () => console.log('Moltbook webhook listening on :3000'));

6.2 Message Formatting

The payload we send back to Moltbook after an approval looks like this:


{
  "requestId": "abc123",
  "status": "in_progress",
  "details": "Agent execution started at 2024-03-23T12:34:56Z"
}

We use the ElevenLabs AI voice integration to optionally generate an audible alert for on‑call engineers.

6.3 Testing the Integration

UBOS provides a sandbox environment. Follow these steps:

  1. Deploy the webhook server locally (or to a UBOS staging instance).
  2. Configure Moltbook to point to https://your‑instance.com/moltbook/webhook.
  3. Trigger a dummy remediation via OpenClaw’s test endpoint.
  4. Verify that the dashboard receives the request, you can approve it, and Moltbook logs the in_progress message.

7. Deployment Steps

When the codebase is ready, use the UBOS solutions for SMBs CI/CD pipeline:

  • Containerize the React app with Dockerfile (multi‑stage build).
  • Push the image to UBOS Container Registry.
  • Deploy via the Enterprise AI platform by UBOS using a Helm chart that also provisions the Moltbook webhook service.
  • Enable auto‑scaling based on queue length (a metric exposed by the dashboard).

After deployment, monitor logs through the UBOS pricing plans dashboard to ensure the webhook latency stays under 200 ms.

8. Conclusion

Building a human‑approval dashboard for OpenClaw remediation agents blends three core capabilities:

  1. Clear, Tailwind‑styled UI that surfaces risk‑critical requests.
  2. Robust API integration that respects UBOS‑managed authentication.
  3. Seamless Moltbook notification flow that keeps operators in the loop.

By following the steps above, developers can deliver a secure, auditable remediation pipeline that satisfies compliance teams while preserving the speed of automated response. For a ready‑made starter, explore the UBOS templates for quick start or the AI Chatbot template for inspiration on modal handling.

Need a hosted version of OpenClaw? Check the dedicated page here for deployment options and pricing.


Andrii Bidochko

CTO UBOS

Andrii Bidochko is an AI entrepreneur and researcher focused on AI agents, reinforcement learning, and autonomous systems. He writes about the technologies shaping the future of machine intelligence, from frontier models and agent architectures to real-world AI applications.

Sign up for our newsletter

Stay up to date with the roadmap progress, announcements and exclusive discounts feel free to sign up with your email.

Sign In

Register

Reset Password

Please enter your username or email address, you will receive a link to create a new password via email.