TxScript for Mini Apps

Learn how to use uiTx SDK to build interactive mini-apps

Overview

uiTx is the client-side SDK for building Mini Apps - custom HTML/CSS/JavaScript applications that run in a sandboxed iframe and can interact with 1Flow data.

Mini Apps are perfect for:

  • Interactive dashboards and reports
  • Custom data exploration tools
  • Workflow automation interfaces
  • Data visualization

Quick Start

Embedded Mode (iframe)

Mini Apps run inside the 1Flow web app in an iframe. Load the SDK via a script tag:

<!DOCTYPE html>
<html>
<head>
  <title>My Mini App</title>
  <style>
    body { font-family: system-ui; padding: 20px; }
  </style>
</head>
<body>
  <h1>My Mini App</h1>
  <div id="app">Loading...</div>
  
  <!-- Load the SDK -->
  <script src="https://zdocs.io/sdk/ui-tx/v1.js"></script>
  
  <script>
    const { uiTx } = window.zDocs;
    
    async function loadData() {
      const results = await uiTx.searchEntities({ 
        query: "AWS", 
        limit: 10 
      });
      
      const app = document.getElementById("app");
      app.innerHTML = results.results.map(item => `
        <div>
          <strong>${item.preview.description || "No description"}</strong><br>
          <small>${item.preview.date || ""} • $${item.preview.amount || 0}</small>
        </div>
      `).join("");
    }
    
    loadData().catch(err => {
      document.getElementById("app").innerHTML = `Error: ${err.message}`;
    });
  </script>
</body>
</html>

Core API

Session & Context

// Get current session info
const session = await uiTx.getSession();
// Returns: { orgId, userId, orgCurrency, permissions, mode, appId, runId }

Search Operations

// Search entities
const results = await uiTx.searchEntities({
  query: "AWS",
  limit: 25,
  filters: {
    docType: "INVOICE",
    date: { gte: "2025-01-01", lte: "2025-12-31" },
    amount: { gte: 100 },
    currency: "USD"
  },
  orderBy: { field: "date", dir: "desc" }
});

// Results structure:
// {
//   total: number,
//   results: Array<{
//     ref: { entityId, documentId, schemaType, ... },
//     preview: { date, amount, currency, description }
//   }>
// }

Entity Operations

// Get full entity data with schema
const entity = await uiTx.getEntity({ entityId: "doc123_items_0" });
// Returns: { ref, data, schema, document }

// List documents
const documents = await uiTx.listDocuments({
  docType: "INVOICE",
  folderId: "folder123",
  date: { gte: "2025-01-01" },
  orderBy: { field: "documentDate", dir: "desc" }
});

Aggregation

// Aggregate data
const aggregated = await uiTx.aggregate({
  from: "entities",
  metrics: [
    { op: "sum", field: "amount", as: "total" },
    { op: "count", as: "count" }
  ],
  groupBy: ["month", "docType"],
  where: [
    { field: "docType", op: "=", value: "INVOICE" }
  ],
  limit: 500
});

// Returns: { rows: Array<Record<string, unknown>> }

Mutations

// Update entity fields
const result = await uiTx.updateEntityFields({
  entityId: "doc123_items_0",
  changes: {
    description: "Updated description",
    category: "Electronics"
  }
});

if (result.ok) {
  console.log("Updated successfully");
} else {
  console.error("Error:", result.error);
}

Reconciliation

// Create reconciliation link
const linkResult = await uiTx.createReconciliationLink({
  sourceRef: { entityId: "invoice123", ... },
  targetRef: { entityId: "payment456", ... },
  amount: 1000.00,
  currency: "USD",
  edgeType: "paid_by"
});

// List reconciliation links
const links = await uiTx.listReconciliationLinks({
  entityId: "invoice123",
  limit: 50
});

// Trace money flow
const trace = await uiTx.traceReconciliation({
  startEntityId: "invoice123",
  direction: "both",
  maxDepth: 10,
  limit: 50
});

UI Helpers (Embedded Mode Only)

// Show toast notification
await uiTx.ui.toast({ 
  message: "Operation completed", 
  level: "success" 
});

// Show confirmation dialog
const confirmed = await uiTx.ui.confirm({
  title: "Confirm Action",
  message: "Are you sure you want to proceed?",
  confirmText: "Yes",
  cancelText: "No"
});

if (confirmed.confirmed) {
  // User confirmed
}

// Open entity in main app
await uiTx.ui.openEntity({ entityId: "doc123_items_0" });

// Open document in main app
await uiTx.ui.openDocument({ documentId: "doc123" });

// Open folder in main app
await uiTx.ui.openFolder({ folderId: "folder123" });

// Upload file to folder
await uiTx.ui.uploadToFolder({ folderId: "folder123" });

Complete Example: Monthly Spending Dashboard

<!DOCTYPE html>
<html>
<head>
  <title>Monthly Spending Dashboard</title>
  <style>
    body { font-family: system-ui; padding: 20px; max-width: 1200px; margin: 0 auto; }
    .chart-bar { height: 30px; background: var(--primary, #007bff); margin: 4px 0; border-radius: 4px; }
    .item { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; }
    .item:hover { background: #f5f5f5; }
  </style>
</head>
<body>
  <h1>Monthly Spending by Category</h1>
  <div id="chart"></div>
  
  <script src="https://zdocs.io/sdk/ui-tx/v1.js"></script>
  <script>
    const { uiTx } = window.zDocs;
    
    async function loadDashboard() {
      try {
        // Get current month
        const now = new Date();
        const firstDay = new Date(now.getFullYear(), now.getMonth(), 1)
          .toISOString().split('T')[0];
        const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0)
          .toISOString().split('T')[0];
        
        // Aggregate spending by category
        const data = await uiTx.aggregate({
          from: "entities",
          metrics: [{ op: "sum", field: "amount", as: "total" }],
          groupBy: ["category"],
          where: [
            { field: "docType", op: "=", value: "INVOICE" },
            { field: "date", op: ">=", value: firstDay },
            { field: "date", op: "<=", value: lastDay }
          ]
        });
        
        // Display chart
        const chart = document.getElementById("chart");
        const maxVal = Math.max(...data.rows.map(r => r.total || 0));
        
        chart.innerHTML = data.rows.map(row => `
          <div style="display: flex; align-items: center; gap: 10px; margin: 8px 0;">
            <span style="width: 150px;">${row.category || "Uncategorized"}</span>
            <div class="chart-bar" style="width: ${(row.total / maxVal) * 400}px;"></div>
            <span>$${row.total.toLocaleString(undefined, { minimumFractionDigits: 2 })}</span>
          </div>
        `).join("");
        
      } catch (error) {
        document.getElementById("chart").innerHTML = `Error: ${error.message}`;
      }
    }
    
    loadDashboard();
  </script>
</body>
</html>

Standalone Mode (HTTP)

For local development or external integrations, you can use the HTTP transport:

import { UiTx, HttpTransport } from "@zdocs/ui-tx";

const uiTx = new UiTx(
  new HttpTransport({
    baseUrl: "https://zdocs.io",
    apiKey: "zdocs_xxxxx",
  })
);

const results = await uiTx.searchEntities({ query: "AWS", limit: 10 });

Security & Limits

  • Rate limits: Maximum 100 search requests per minute
  • Execution timeout: 30 seconds per operation
  • Data access: All data is read-only (cloned values)
  • Validation: All inputs validated server-side

Theme Support

Mini Apps can detect the parent theme:

const theme = window.__PARENT_THEME__; // "dark" or "light"

if (theme === "dark") {
  document.body.style.background = "#1a1a1a";
  document.body.style.color = "#fff";
}

Best Practices

  1. Always handle errors: Wrap API calls in try-catch blocks
  2. Use preview data: For lists, use preview fields for better performance
  3. Cache results: Store frequently accessed data in variables
  4. Use UI helpers: Leverage ui.toast() and ui.confirm() for better UX
  5. Open entities properly: Use ui.openEntity() instead of manual navigation