GenuineAIGenuineAI
For usersFor developers
  • Overview
  • Guides
  • How-to
  • API reference
Documentation
  • Quickstart
  • Authentication
  • Errors
  • Rate limits
  • API reference
Platform
  • Platform overview
  • Solutions
  • Pricing
  • Sign in
Company
  • Status
  • Trust Center
  • Contact
  • Terms of Service
  • Privacy Policy

© 2026 GenuineAI Ventures LLC

support@genuinehq.com
Media library
Uploading filesOrganizing and finding assetsDelivering assetsPublishing assets
AI
AI enrichmentPeople and facesAgents and conversations
Knowledge
Knowledge basesHow agents use knowledge
Automation
Autonomous AIExternal dataExtensions
Creating and deploying
Generating contentDeployed AI
Operations
Usage and credits
How-to

Extensions

An extension is a service you run that adds to one workspace: a page in the menu, a panel on a record, a tool its agents can call, or work that starts when something changes. It runs on your infrastructure, reaches the workspace only through this API, and shows its pages inside the app in a frame. Nothing it does runs inside the platform.

Gated on the module-extensions entitlement. Extensions are registered by GenuineAI and offered to a workspace; a workspace administrator then enables each one and chooses what it may do.

The manifest

Everything the platform knows about an extension is in its manifest, extension.json. Its JSON Schema ships with the extension SDK as @genuineai/ext-sdk/schema.

Code
{ "slug": "warranty", "name": "Warranty triage", "version": "1.4.0", "scopes": ["object:view", "object:edit", "extension-state:read", "extension-state:write"], "ui": { "origin": "https://warranty.example.com", "contributions": [ {"slot": "menu.page", "path": "/claims", "title": "Warranty claims", "icon": "ph-wrench"}, {"slot": "object.item.panel", "path": "/panel", "title": "Triage", "objectTypes": ["warranty-claim"]} ] }, "events": {"url": "https://warranty.example.com/events", "types": ["object.created", "object.state_changed"]}, "tools": {"url": "https://warranty.example.com/tools", "items": [ {"name": "claim_history", "description": "Claims on file for a serial number", "input_schema": {"type": "object", "properties": {"serial": {"type": "string"}}, "required": ["serial"]}} ]}, "egress": ["erp.example.com"] }
FieldWhat it decides
scopesThe permissions the extension asks for. An administrator grants any subset they hold themselves. Administration of users, roles, settings, keys and billing, the audit and log views, and other extensions can never be granted.
uiWhere its pages are served from, and which slots they fill.
eventsWhich changes in the workspace it hears about, and where they are sent.
toolsTools the workspace's agents may call, with the input each one takes.
egressThe outside hosts it talks to, shown to the administrator who enables it.

Two identities

The install key. Enabling an extension issues a key that acts as the extension itself, with exactly the scopes the administrator granted, narrowed to the workspace's plan. Use it for background work: handling events, syncing a system, writing results.

TerminalCode
curl https://api.genuineai.app/api/v1/extensions \ -H "X-Api-Key: gai_…" -H "X-Tenant-Id: <workspace-id>"

A delegated token. When a person opens an extension page, or an agent working for a person calls a tool, the platform hands the extension a token for that person. It is signed with RS256, lasts ten minutes, and lets the extension act as that person: what the person may do today, narrowed to the extension's scopes. The audit trail names the person. Verify it against the published keys:

Code
GET https://api.genuineai.app/.well-known/jwks.json

The token's audience is ext:<slug>. Send it as Authorization: Bearer <token> to call the API for that person. Neither identity reaches workspace administration.

Pages and slots

A page is served from your ui.origin and framed by the app. The frame and the app talk through postMessage, and each side drops messages from any other origin.

SlotWhere it appearsWhat the page is told
menu.pageA menu entry and a full pageNothing beyond the person
object.item.panelA panel on a record; objectTypes limits the kindsThe record id and kind
document.item.tabA tab on a documentThe document id
chat.context.sectionA section beside a conversationThe conversation id
dashboard.tileA card on the dashboardNothing beyond the person
admin.tabA tile in workspace administrationNothing beyond the person

The page announces itself with ready; the app answers with init, which carries the token, the person, the workspace, the light or dark theme and the colors that make a page look native. The page can ask the app to resize the frame, navigate, show a message, reload a record it changed, and refresh the token. The SDK's connect() does all of this:

Code
import {connect} from '@genuineai/ext-sdk/bridge'; const bridge = await connect(); const claim = await bridge.fetch(`/objects/${bridge.context.subject.id}`);

Serve every page with Content-Security-Policy: frame-ancestors naming the app's hosts.

Events

When a subscribed change happens, the platform POSTs an envelope to events.url:

Code
{ "id": "0b7e…", "type": "object.state_changed", "occurred_at": "2026-10-03T14:20:03Z", "sequence": 48211, "tenant": "8b1f…", "subject": {"type": "object", "id": "a3c9…"}, "actor": {"type": "user", "id": "77d0…"}, "data": {"to": "c41e…"} }

The envelope carries ids, never record contents: read the record through the API with the install key. X-GenuineAI-Signature: t=<unix time>,v1=<hex> is an HMAC-SHA256 of <t>.<raw body> with the install's signing secret; refuse anything that does not verify or is more than five minutes old. Answer with any 2xx once the work is done. Anything else is retried with growing gaps for about an hour, then kept as failed, where an administrator can send it again. Deliveries can repeat and arrive out of order: treat id as the idempotency key, and sequence to discard an older change to a subject you have already applied.

Event types follow resource.action: object.created, object.updated, object.state_changed, file.created, task.status_changed, task.assigned, form.submitted and others. Nothing is sent for a type no enabled extension subscribes to.

Agent tools

Each tool in the manifest becomes available to the workspace's agents as ext:<slug>:<name>. When an agent calls it, the platform POSTs to <tools.url>/<name>:

Code
{"tool": "claim_history", "arguments": {"serial": "7HX-44019-C"}, "tenant": "8b1f…", "user": "77d0…", "thread": "…"}

The request is signed like an event, and carries a delegated token for the person the agent is working for when there is one. Answer within 30 seconds with JSON; answers past about 16,000 characters are shortened before the model reads them.

State

Most extensions need somewhere to keep a decision, a cursor or a draft, and nothing more. Each extension can keep up to 1,000 JSON documents of up to 256 KB in a workspace:

Code
GET /extensions/{slug}/state GET /extensions/{slug}/state/{key} PUT /extensions/{slug}/state/{key} {"exs_value": …} DELETE /extensions/{slug}/state/{key}

Every read returns the document's version as an ETag. Send it back as If-Match and a write made since is refused with 412 instead of overwritten.

Developing

The extension template starts a local server, a public tunnel and a coding session with one command:

Code
npx genuineai dev

In a sandbox workspace, Administration → Extensions → Open the Workbench pairs with it. The sandbox install then sends its events and tool calls to the tunnel and frames the dev pages, so a change is visible in the app as soon as it is saved. Dev builds can be connected only in workspaces marked as sandboxes, and the connection ends on its own after twelve hours.

Last modified on October 8, 2026
External dataGenerating content
On this page
  • The manifest
  • Two identities
  • Pages and slots
  • Events
  • Agent tools
  • State
  • Developing
JSON
Javascript
JSON
JSON