What you can build

What you can build

A Telenow app is a manifest (telenow.app.json) plus an optional UI bundle (a sandboxed React dashboard) and an optional backend (your own server reached over the app-key REST API). One app can mix tiers freely — declarative data objects, an http tool, a sandbox (pure-compute) tool, a dashboard page, and durable automation can all live in the same app.

This page is the map of everything the platform lets you build, organised by the outcome you want. Each pattern lists the capabilities and scopes it uses and links to the deep page where you actually build it. Skim it to find your shape, then follow the link.

🤖 Using an AI assistant to build? Hand it the App-Building Skill file — a single, self-contained set of rules + reference an LLM can follow to build a correct Telenow app. Paste its link or contents into your assistant and tell it to follow the framework.


1. A CRM the agent shares

Declare your data types as objects in the manifest and the platform gives you a hosted, multi-tenant store — no database, no hosting. Add object.create / object.query / object.update / object.delete tools and the voice agent can read and write that same data live during a call (find a patient by phone, book an appointment, log a visit). Objects support relations (relation: { object, many? }, expandable on read), computed read-only fields (computed: { template } with {{field}} interpolation), saved views (views[] — a named filter + sort), and opt-in semantic search (semantic: true → rows embedded on write, queried with ?search=).

This is the Doctor CRM shape: objects patient / appointment / visit / lead, eight object.* tools, and an agent that uses them on every call. Build it in Data & objects and Agent tools.

2. A native dashboard inside Telenow

Ship a React UI bundle that renders inside the Telenow dashboard as its own sidebar pages (and/or inside platform extension slots). The bundle runs in a sandboxed iframe on an opaque origin — no cookies, no tokens, no API keys in the browser — and reads/writes your app's data through the injected window.telenow bridge (relayed to the parent and enforced server-side under the signed-in user). Use the React hooks (useObjects, useUser, useSettings, …) and the built-in design system (--tn-* CSS tokens + .tn-* classes, synced to light/dark) so the app looks native.

Pages mount as sidebar items (ui.pages[] with menu: true); extensions mount one of your pages into a named surface via ui.extensions[]. The six mountable slots are call_detail_panel, dashboard_widget, agent_builder_panel, agents_overview_panel, call_list_panel, and softphone_call_panel. Build it in Dashboard UI.

3. No-backend lead → call / message automation

Turn an inbound event into action with zero hosting. An inbound webhook or an object.<type>.created event triggers a durable workflow — a persisted, retrying state machine whose steps run on Telenow's runtime. Step kinds are exactly: create-object, update-object, delay, outbound-call (place an agent call), send-message (WhatsApp), http (SSRF-guarded), and connector (call a connected integration — Google Sheets, a CRM — with the org's own credentials). Steps interpolate {{trigger.*}} and {{steps.N.*}} from the run context, so a new lead row can flow straight into an outbound call or a WhatsApp message.

This is the classic lead lands → agent calls them back in 2 minutes loop with no server of your own. Build it in Events, hooks & workflows.

4. A 3rd-party sync / external tool

Reach an external HTTPS API two ways. Use a tool with handler.kind: "http" to POST the call's tool invocation to your own backend (set base_url; verify the X-Telenow-Signature), or call a public API directly through the dashboard's server-side proxy with telenow.http({ url, method, headers, body }). The proxy is HTTPS-only, host-allowlisted via your http:<host> scopes, SSRF-guarded, follows no redirects, and caps responses at 1 MB.

Inject credentials without ever seeing them: per-install secret settings (settings[] with secret: true, encrypted, server-side only) or stored connections (connection: "<provider>" injects an org's auto-refreshed OAuth credential server-side). Build it in Agent tools and Scopes & security.

5. A live wallboard / call monitor

Build a real-time view that updates without polling. telenow.data.subscribe(type, onChange) streams every create / update / delete to your data as { event, objectType, id, data } frames. telenow.stream.subscribe(sessionId, onEvent) streams mid-call events — partial transcript, turns, barge-in, silence, DTMF, node-entered — while a call is in progress (the host holds the WebSocket; your iframe never opens one). Both live-call surfaces require the calls:read scope.

This is a wallboard, a live-transcript panel, or a "calls happening now" widget. Build the realtime data side in Dashboard UI and the mid-call event model in Events, hooks & workflows.

6. A one-click agent (+ bundled KB) you distribute

Ship ready-made voice agents in the manifest. agents[] defines a full agent spec (flattened: systemPrompt, llmProvider/llmModel, ttsVoice, sessionConfig, and a metadata.flow graph for multi-context agents); one click builds a real agent auto-bound to your app's tools. agentTeams[] builds a whole multi-agent team with handoffs rewired to real agent ids. knowledgeBases[] ships documents that are embedded on install and auto-attached to every agent your app builds, so the agent does RAG at call time.

Then keep quality high with the eval harness: POST /api/app-agents/:id/eval runs scenarios through a headless simulation and an LLM-judge for pass/fail in CI. Build it in Bundled agents & knowledge.

7. A bulk outbound campaign over your own data

Launch paced, retried bulk outbound calls from your backend over the app-key REST API: POST /api/app-campaigns with either an explicit targets[] (each { phone, variables? }) or a targetQuery over your own objects ({ object, phoneField, filter }). Tune retryBackoffSecs (default 300), retryOnNoAnswer (default true), and autostart (default true); pause/cancel and poll status via the campaign endpoints. Each call's outcome can be written back into your app objects via the campaign's result config.

This needs the campaigns:write / campaigns:read scopes and an app-bound agent. Build it in External backends & the app-key API.

8. An inbound-webhook-driven app

Receive POSTs from a 3rd party (Meta lead-ads, Stripe, Calendly, …) with no dev backend at all. Declare inboundHooks[] and the platform hosts a receiver at POST /webhooks/app/<installationId>/<id>: it verifies the request, field-maps the body into an object, and fires object.<type>.created so your workflows/rules pick it up. Verification is either HMAC (verify: { header, algo, … }, e.g. Meta's x-hub-signature-256) or an unguessable ?token=<signing secret>. For Meta you also get the GET hub.challenge handshake automatically, so you can pass Meta's webhook verification step.

This is the Meta lead → lead object → workflow → callback loop. Build it in Events, hooks & workflows.

9. A scheduled job

Run something on a fixed interval with schedules[]. Each schedule ({ key, every, handler }) fires either a declarative rule (upsert into an object) or a webhook (ping your backend). every is a simple duration (30m | 1h | 6h | 24h | 1d), minimum 5 minutes, up to 20 schedules per app. Use it for periodic syncs, digests, or housekeeping. Build it in Events, hooks & workflows.

10. A backend integration via app keys

Drive Telenow programmatically from your own server with an app key (Authorization: Bearer <app key>), bound to one (org, app) so every call is automatically tenant-scoped. The app-key REST API covers Data (/api/app-data), Files (/api/app-files), Agents (/api/app-agents, incl. eval), Campaigns (/api/app-campaigns), and live-call stream tickets. Verify platform → you calls with verifySignature(signingSecret, rawBody, header) (the X-Telenow-Signature header), and mint short-lived identity JWTs for your backend with telenow.session.token() (verify with verifyAppToken).

Build it in External backends & the app-key API.


How tiers combine

The three runtime tiers — declarative (no app code; objects + object.* tools + UI + automation run on Telenow), external (your backend behind http tools + the app-key API), and sandboxed (pure-compute js/sandbox tools) — are not exclusive. A single app commonly mixes them: declarative objects for the data, one http tool that calls a partner API, a sandbox tool for a deterministic calculation, a dashboard page, and a workflow for follow-ups. See App platform overview for how a manifest, UI bundle, and backend fit together.

Know the limits

Every surface has quotas — 100,000 rows per app, 25 MB per blob (1 GB / 10,000 blobs total), max 20 schedules / workflows / inbound hooks / knowledge bases, tool timeoutSecs clamped 1–30s (default 15), a 1 MB HTTP-proxy cap, and per-(org, app) rate limits. Check them before you design around a number: see Limits & quotas.


A multi-tier app in one manifest

The example below is intentionally tiny but mixes all three tiers — declarative objects, an external http tool, and a sandboxed compute tool — in a single telenow.app.json:

{
  "id": "clinic-crm",
  "version": "2.1.2",
  "name": "Doctor CRM",
  "runtime": "external",
  "base_url": "https://clinic.example.com",
  "scopes": ["objects:patient", "calls:read", "http:api.example.com"],
  "objects": [
    { "type": "patient", "label": "Patient", "fields": [
      { "key": "name", "type": "string", "index": true },
      { "key": "phone", "type": "phone", "index": true }
    ]}
  ],
  "tools": [
    {
      "name": "find_patient",
      "description": "Look up a patient by phone number.",
      "handler": { "kind": "object.query", "object": "patient" }
    },
    {
      "name": "verify_insurance",
      "description": "Check insurance with our backend.",
      "handler": { "kind": "http", "path": "/insurance/verify", "method": "POST" }
    },
    {
      "name": "estimate_copay",
      "description": "Compute the patient copay (pure math, no I/O).",
      "handler": { "kind": "sandbox", "code": "return { copay: dv.total * dv.rate };" }
    }
  ]
}

find_patient is declarative (runs in-core), verify_insurance POSTs the signed tool call to your base_url, and estimate_copay runs in the hardened pure-compute sandbox.


Next