Agent skill

platform-api

Platform API reference for DenchClaw apps — UI integration, per-app storage, HTTP proxy, real-time events, inter-app messaging, cron scheduling, webhooks, clipboard, context, and widget mode.

Stars 1,503
Forks 102

Install this agent skill to your Project

npx add-skill https://github.com/DenchHQ/DenchClaw/tree/main/skills/app-builder/platform-api

Metadata

Additional technical details for this skill

openclaw
{
    "emoji": "\u26a1",
    "always": true,
    "inject": true
}

SKILL.md

App Platform API

This skill documents the platform-level APIs available to DenchClaw apps. For core app structure, see the parent app-builder skill. For data/objects, see data-builder. For AI chat, see agent-builder.

UI Integration (ui permission required)

javascript
// Show a toast notification in the parent DenchClaw UI
await dench.ui.toast("Record saved successfully", { type: "success" });
await dench.ui.toast("Something went wrong", { type: "error" });
await dench.ui.toast("Processing...", { type: "info" });

// Navigate DenchClaw to a workspace path (opens object, file, or app)
await dench.ui.navigate("/people");           // open the people object
await dench.ui.navigate("/apps/my-app.dench.app"); // open another app

// Open an entry detail modal
await dench.ui.openEntry("people", "entry_id_here");

// Update the app's tab title dynamically
await dench.ui.setTitle("My App — 5 results");

// Show a confirmation dialog
const confirmed = await dench.ui.confirm("Delete this record?");
if (confirmed) { /* proceed */ }

// Show a prompt dialog
const name = await dench.ui.prompt("Enter a name:", "Default Name");
if (name !== null) { /* use name */ }

Per-App KV Store (store permission required)

Persistent key-value storage scoped to each app. Data survives app reloads and is stored in the workspace.

javascript
// Store a value (any JSON-serializable value)
await dench.store.set("lastQuery", { sql: "SELECT * FROM people", timestamp: Date.now() });
await dench.store.set("theme", "custom-dark");
await dench.store.set("counter", 42);

// Read a value
const lastQuery = await dench.store.get("lastQuery");
// Returns the value, or null if not found

// Delete a key
await dench.store.delete("lastQuery");

// List all keys
const keys = await dench.store.list();
// Returns ["theme", "counter"]

// Clear all stored data
await dench.store.clear();

Storage is backed by a JSON file at {workspace}/.dench-app-data/{appName}/store.json. Each app gets its own isolated namespace.

HTTP Proxy (http permission required)

Make HTTP requests from apps without CORS restrictions. Requests are proxied through the DenchClaw server.

javascript
// Simple GET
const data = await dench.http.fetch("https://api.example.com/data");
// Returns { status, statusText, headers, body }

// POST with headers and body
const result = await dench.http.fetch("https://api.example.com/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer sk-..."
  },
  body: JSON.stringify({ query: "test" })
});

console.log(result.status);  // 200
console.log(result.body);    // response body as string

Security: requests to localhost, private IPs, and internal DenchClaw URLs are blocked.

Real-time Events (events permission required)

Subscribe to workspace events for live updates.

javascript
// Subscribe to theme changes
dench.events.on("theme.changed", (data) => {
  document.body.className = data.theme;
});

// Subscribe to object entry changes
dench.events.on("object.entry.created", (data) => {
  console.log(`New entry in ${data.objectName}: ${data.entryId}`);
  refreshList();
});

dench.events.on("object.entry.updated", (data) => {
  console.log(`Entry ${data.entryId} updated in ${data.objectName}`);
});

dench.events.on("object.entry.deleted", (data) => {
  console.log(`Entry ${data.entryId} deleted from ${data.objectName}`);
});

// App visibility events
dench.events.on("app.visible", () => { resumeAnimations(); });
dench.events.on("app.hidden", () => { pauseAnimations(); });

// File change events
dench.events.on("file.changed", (data) => {
  console.log(`File changed: ${data.path}`);
});

// Unsubscribe
dench.events.off("theme.changed");

Context (no permission required)

javascript
// Get workspace info
const workspace = await dench.context.getWorkspace();
// Returns { name, path, agentId }

// Get app info
const app = await dench.context.getAppInfo();
// Returns { appPath, folderName, permissions, manifest }

Inter-App Messaging (apps permission required)

Apps can communicate with other open apps for composite workflows.

javascript
// Send a message to another app
await dench.apps.send("analytics-dashboard.dench.app", {
  action: "refresh",
  filter: { status: "Active" }
});

// Listen for messages from other apps
dench.apps.on("message", (event) => {
  console.log(`Message from ${event.from}:`, event.message);
  if (event.message.action === "refresh") {
    reloadData(event.message.filter);
  }
});

// List currently active (open) apps
const activeApps = await dench.apps.list();
// Returns [{ name: "analytics-dashboard.dench.app", manifest: {...} }, ...]

Cron Scheduling (cron permission required)

Schedule recurring tasks that send messages to the agent.

javascript
// Schedule a cron job
const { jobId } = await dench.cron.schedule({
  expression: "0 9 * * *",          // 9 AM daily
  message: "Generate the daily sales report and save it to workspace",
  channel: "announce"                 // "announce" (delivers result) or "none" (silent)
});

// List all cron jobs
const jobs = await dench.cron.list();
// Returns array of { id, expression, message, enabled, nextRunAt, ... }

// Run a job immediately
await dench.cron.run(jobId);

// Cancel/remove a job
await dench.cron.cancel(jobId);

Webhooks (webhooks permission required)

Receive external HTTP webhooks inside your app.

javascript
// Register a webhook endpoint
const hook = await dench.webhooks.register("github-push");
console.log(hook.url);
// e.g. "https://your-denchclaw-host/api/apps/webhooks/my-app.dench.app/github-push"

// Listen for incoming webhooks
dench.webhooks.on("github-push", (payload) => {
  console.log("Received webhook:", payload);
  // payload: { method: "POST", headers: {...}, body: "...", receivedAt: 1234567890 }
  processGithubPush(JSON.parse(payload.body));
});

// Poll for webhooks (useful for catching events received while app was closed)
const events = await dench.webhooks.poll("github-push", { since: lastTimestamp });

Clipboard (clipboard permission required)

javascript
// Write to clipboard
await dench.clipboard.write("Copied text content");

// Read from clipboard
const text = await dench.clipboard.read();

Note: clipboard operations are proxied through the parent DenchClaw window.

Widget Mode

Apps can render as compact widgets in a dashboard grid instead of full-page tabs.

Manifest Configuration

yaml
name: "Quick Stats"
display: "widget"
widget:
  width: 2              # Grid columns (1-4)
  height: 1             # Grid rows (1-4)
  refreshInterval: 60   # Auto-refresh in seconds (optional)
permissions:
  - database

Widget Design Guidelines

  • Keep the UI compact — widgets have limited space
  • Use large, readable numbers and minimal text
  • Avoid scroll bars — all content should be visible
  • Support both light and dark themes
  • Use the refresh interval for auto-updating data

Widget Example

html
<!DOCTYPE html>
<html>
<head>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, sans-serif;
      padding: 16px;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    body.dark { background: #0f0f1a; color: #e8e8f0; }
    body.light { background: #fff; color: #1a1a2e; }
    .metric { font-size: 48px; font-weight: 700; }
    .label { font-size: 13px; opacity: 0.6; margin-bottom: 4px; }
  </style>
</head>
<body>
  <div class="label">Total Records</div>
  <div class="metric" id="count">—</div>
  <script>
    async function init() {
      const theme = await dench.app.getTheme().catch(() => "dark");
      document.body.className = theme;
      
      const result = await dench.db.query("SELECT SUM(entry_count) as total FROM objects");
      document.getElementById("count").textContent = result.rows[0]?.total ?? 0;
    }
    init();
  </script>
</body>
</html>

Widget-mode apps appear in the DenchClaw dashboard view alongside other widgets, arranged in a responsive grid.

Patterns

Multi-App Dashboard

Build a dashboard that aggregates data from multiple widget apps:

javascript
// In the main dashboard app
async function loadWidgetData() {
  const apps = await dench.apps.list();
  const widgets = apps.filter(a => a.manifest.display === "widget");
  
  for (const widget of widgets) {
    // Request data from each widget app
    await dench.apps.send(widget.name, { action: "getData" });
  }
}

dench.apps.on("message", (event) => {
  if (event.message.action === "dataResponse") {
    updateDashboardPanel(event.from, event.message.data);
  }
});

External API Integration

javascript
// Fetch data from an external API via proxy
async function loadWeather(city) {
  const result = await dench.http.fetch(
    `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=YOUR_KEY`
  );
  if (result.status === 200) {
    const weather = JSON.parse(result.body);
    displayWeather(weather);
  }
}

Automation Workflow

javascript
// Set up a cron job that processes data and sends results
async function setupAutomation() {
  const { jobId } = await dench.cron.schedule({
    expression: "0 */6 * * *",
    message: "Check the tasks object for overdue items and send a summary to Telegram"
  });
  
  await dench.store.set("automationJobId", jobId);
  dench.ui.toast("Automation scheduled every 6 hours", { type: "success" });
}

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results