Appearance
Apps & manifests
Every app on NeodyAI — ours (CRM, Work, Documents, Billing) and yours — is described by a manifest. The platform reads it to show the app in the workspace, route its API, check permissions, register its agent tools and decisions, and wire its events. First-party apps use exactly the same contract.
Status
Publishing third-party apps to the marketplace is in preview. Contact us to build one; the manifest below is the contract you build against.
Example
json
{
"id": "acme.inventory",
"name": "Inventory",
"version": "1.0.0",
"publisher": "acme",
"description": "Stock levels, reorder points and transfers.",
"icon": "Boxes",
"category": "ops",
"ui": {
"web": { "entry": "https://apps.acme.example/inventory/", "basePath": "/inventory" },
"nav": [{ "id": "inv-stock", "label": "Stock", "icon": "Boxes", "path": "/inventory/stock" }]
},
"api": { "baseUrl": "https://api.acme.example/neody" },
"scopes": ["inventory.read", "inventory.write", "core.contacts.read"],
"tools": [
{
"name": "inventory.reserve_stock",
"description": "Reserve stock for an order.",
"input_schema": {
"type": "object",
"required": ["sku", "quantity"],
"properties": { "sku": { "type": "string" }, "quantity": { "type": "integer", "minimum": 1 } }
},
"risk": "medium",
"requires_approval": false
}
],
"decisions": [
{
"name": "inventory.classify_shortage",
"description": "How urgent is this shortage?",
"input_schema": { "type": "object", "properties": { "sku": { "type": "string" }, "days_left": { "type": "number" } } },
"output_schema": { "type": "string", "enum": ["urgent", "soon", "fine"] },
"thresholds": { "auto": 0.9, "suggest": 0.6 }
}
],
"events": { "emits": ["inventory.stock.low"], "consumes": ["crm.deal.won"] }
}Fields
| Field | Required | Rules |
|---|---|---|
id | ✓ | publisher.slug, matching ^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$. Never changes. neody.* is reserved. |
name | ✓ | 1–40 characters |
version | ✓ | Semantic version |
publisher | ✓ | Equal to the prefix of id |
description | ✓ | Up to 280 characters |
icon | ✓ | An icon name or an https:// URL to an SVG |
category | ✓ | sales, work, finance, security, support, ops, hr, marketing, developer or other |
ui.web.entry | with a UI | Where the app's web UI loads from |
ui.web.basePath | with a UI | ^/[a-z][a-z0-9-]*$, unique in the workspace. The app appears at /app{basePath} |
ui.nav[] | Up to 12 {id, label, icon, path}; every path starts with basePath | |
ui.quickAdd[] | Same shape; shown in the "+" menu and the command palette | |
api | ✓ | { "baseUrl": "https://…" } for your service; calls are proxied with signed headers |
scopes[] | ✓ | Your own slug.* scopes plus platform (core.*) or other apps' scopes you request. An admin consents at install |
tools[] | Agent tools: name is slug.verb_noun, input_schema is JSON Schema 2020-12, risk is low, medium or high | |
decisions[] | Typed decisions with a closed output_schema (an enum, boolean, bounded number, or object of those) and thresholds | |
events.emits[] / events.consumes[] | Event names slug.entity.verb, past tense |
Rules the platform enforces
- Every tool, decision, event and own scope starts with your app's slug.
- Tools with
risk: highalways need a person's approval (requires_approvalis forced on). - A decision's
thresholds.autois greater thanthresholds.suggest. Workspace admins can raise them, never lower them below the platform floor. - Decision outputs are closed types, never free text, so they can be checked and measured.
- There is no scope that lets an app read stored secrets.
Tools, decisions and people
- An agent tool is an action an AI agent may take through your app. Its
riskdecides whether a person must approve it; high-risk tools always wait for a person. - A decision is a typed judgement (classify, score, route). Above
autoconfidence it can act; betweensuggestandautoit proposes and a person confirms; belowsuggestit asks a person.
Versions
Minor and patch versions update automatically and may add tools, decisions, navigation, events or optional scopes (new scopes need consent again before use). Removing or renaming anything, or changing a schema incompatibly, needs a new major version; workspaces opt in, and the previous major stays supported for at least 90 days.
Embedding
Third-party web UIs load in a sandboxed frame on their own origin and talk to the workspace through a postMessage SDK: a short-lived token scoped to your app (never passed in a URL), navigation, notifications, opening records and invoking agents.