Manifest Reference
The manifest.json is the single source of truth for your application. It declares your entities, fields, permissions, and actions. Everything else — tables, CRUD API, constraints — is derived from it automatically.
Top-level fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
appId |
string |
Yes | Unique identifier. Becomes the PostgreSQL schema name. Immutable after first deploy. | |
name |
string |
Yes | Display name shown in the Studio. | |
type |
string |
No | "app" |
Application type: "app" (default) or "integration". |
version |
string |
No | "0.0.1" |
Semantic version for deployment history. |
description |
string |
No | "" |
Short description shown in the Studio. |
dataContract |
array |
No | [] |
Array of entity definitions. |
permissions |
object |
No | Object with a permissions array of permission keys. |
|
actions |
array |
No | [] |
Array of RPC action declarations. |
configSchema |
object |
No | JSON Schema for the app's configuration structure. | |
webhooks |
string[] |
No | [] |
Webhook event names the app can receive. |
instructions |
string |
No | Usage instructions for AI agents interacting with this app. |
appId must be lowercase snake_case (letters, digits, underscores). Hyphens are not allowed. It is immutable after first deploy and becomes the PostgreSQL schema name directly (e.g. appId: "crm" creates schema crm).dataContract[]
Each entry becomes a PostgreSQL table. System columns (id, created_at, updated_at) are added automatically. You can override id with a custom primary key, but created_at and updated_at are always managed by the system and cannot be overridden.
| Field | Type | Required | Description |
|---|---|---|---|
entityName |
string |
Yes | Table name. Used in API routes (/api/v1/apps/{appId}/collections/{entityName}). |
fields |
array |
Yes | Array of field definitions. |
"dataContract": [
{
"entityName": "contacts",
"fields": [
{ "name": "first_name", "type": "text", "required": true },
{ "name": "last_name", "type": "text", "required": true },
{ "name": "email", "type": "text" },
{ "name": "phone", "type": "text" }
]
}
]
dataContract[].fields[]
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string |
Yes | Column name. created_at and updated_at are reserved (system-managed). You may declare a custom id field to override the default UUID primary key. |
|
type |
string |
Yes | One of the field types. | |
required |
boolean |
No | false |
Adds a NOT NULL constraint. |
defaultValue |
any |
No | Default on insert. Accepts JSON values: "draft", 0, false, {"key": "val"}. |
|
enumValues |
string[] |
No | Allowed values. Generates a CHECK constraint. |
|
references |
object |
No | For entity_link only. { "entity": "...", "field": "id" }. Generates a FOREIGN KEY. |
|
isPrimaryKey |
boolean |
No | false |
Custom primary key. Rarely needed. |
Field types
Fields map directly to PostgreSQL types:
| Type | PostgreSQL | Description |
|---|---|---|
text |
TEXT |
Variable-length string. |
number |
DOUBLE PRECISION |
64-bit floating-point. |
boolean |
BOOLEAN |
True/false. |
date |
DATE |
Calendar date (YYYY-MM-DD). |
timestamp |
TIMESTAMPTZ |
Date and time in UTC. |
json |
JSONB |
Arbitrary JSON. Supports GIN indexing. |
file |
TEXT |
File reference from the upload endpoint. |
entity_link |
UUID REFERENCES |
Foreign key. Requires references. |
[text] |
TEXT[] |
Array of strings. |
[number] |
DOUBLE PRECISION[] |
Array of numbers. |
Relationships and constraints
Use entity_link to create foreign keys between entities, enumValues to restrict allowed values, and required to enforce NOT NULL:
{
"name": "stage",
"type": "text",
"enumValues": ["lead", "qualified", "proposal", "won", "lost"],
"defaultValue": "lead"
},
{
"name": "contact_id",
"type": "entity_link",
"references": { "entity": "contacts", "field": "id" }
},
{
"name": "company_id",
"type": "entity_link",
"references": { "entity": "companies", "field": "id" }
}
actions[]
Actions are RPC methods declared in the manifest. Declaring them makes them discoverable by the Studio, integrations, and AI agents.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Must match a key in serve(). |
name |
string |
Yes | Display name for Studio and AI agents. |
description |
string |
No | What the action does. |
inputSchema |
object |
No | JSON Schema for input parameters. |
outputSchema |
object |
No | JSON Schema for the return value. |
To gate access, declare a matching key in the permissions block (e.g. action.pipeline).
"actions": [
{
"id": "pipeline",
"name": "Pipeline Summary",
"description": "Returns deal counts per stage",
"outputSchema": {
"type": "object",
"properties": {
"lead": { "type": "number" },
"qualified": { "type": "number" },
"proposal": { "type": "number" },
"won": { "type": "number" },
"lost": { "type": "number" }
}
}
}
]
permissions
Add permission keys to control access. Keys follow the entity.action convention. Assign them to roles via the Studio Roles & Permissions panel or the RBAC API.
"permissions": {
"permissions": [
{ "key": "contacts.create", "description": "Create contacts" },
{ "key": "contacts.read", "description": "View contacts" },
{ "key": "contacts.update", "description": "Edit contacts" },
{ "key": "contacts.delete", "description": "Delete contacts" },
{ "key": "deals.update", "description": "Edit deals" },
{ "key": "deals.delete", "description": "Delete deals" }
]
}
configSchema
An optional JSON Schema that defines the structure of app-level configuration. Useful for settings that vary per deployment (feature flags, thresholds, display preferences):
"configSchema": {
"type": "object",
"properties": {
"defaultCurrency": { "type": "string", "default": "USD" },
"enableNotifications": { "type": "boolean", "default": true }
}
}
Full example manifest
{
"appId": "sales_crm",
"name": "Sales CRM",
"version": "1.0.0",
"description": "Manage contacts, companies, and deals with a sales pipeline.",
"dataContract": [
{
"entityName": "contacts",
"fields": [
{ "name": "first_name", "type": "text", "required": true },
{ "name": "last_name", "type": "text", "required": true },
{ "name": "email", "type": "text" },
{ "name": "phone", "type": "text" },
{ "name": "company_id", "type": "entity_link", "references": { "entity": "companies", "field": "id" } }
]
},
{
"entityName": "companies",
"fields": [
{ "name": "name", "type": "text", "required": true },
{ "name": "industry", "type": "text" },
{ "name": "website", "type": "text" }
]
},
{
"entityName": "deals",
"fields": [
{ "name": "title", "type": "text", "required": true },
{ "name": "value", "type": "number" },
{ "name": "stage", "type": "text", "enumValues": ["lead", "qualified", "proposal", "won", "lost"], "defaultValue": "lead" },
{ "name": "contact_id", "type": "entity_link", "references": { "entity": "contacts", "field": "id" } },
{ "name": "company_id", "type": "entity_link", "references": { "entity": "companies", "field": "id" } },
{ "name": "notes", "type": "json" }
]
}
],
"actions": [
{
"id": "pipeline",
"name": "Pipeline Summary",
"description": "Returns deal counts per stage",
"outputSchema": {
"type": "object",
"properties": {
"lead": { "type": "number" },
"qualified": { "type": "number" },
"proposal": { "type": "number" },
"won": { "type": "number" },
"lost": { "type": "number" }
}
}
}
],
"permissions": {
"permissions": [
{ "key": "contacts.create", "description": "Create contacts" },
{ "key": "contacts.read", "description": "View contacts" },
{ "key": "contacts.update", "description": "Edit contacts" },
{ "key": "contacts.delete", "description": "Delete contacts" },
{ "key": "companies.create", "description": "Create companies" },
{ "key": "companies.read", "description": "View companies" },
{ "key": "deals.create", "description": "Create deals" },
{ "key": "deals.read", "description": "View deals" },
{ "key": "deals.update", "description": "Edit deals" },
{ "key": "deals.delete", "description": "Delete deals" }
]
}
}
Schema sync
Every manifest install diffs your dataContract against the live PostgreSQL schema and applies the minimum DDL in a single transaction per table:
| Change | DDL |
|---|---|
| New entity | CREATE TABLE |
| New field | ALTER TABLE ... ADD COLUMN |
| Field type changed | ALTER TABLE ... ALTER COLUMN ... TYPE with USING cast |
required changed |
SET / DROP NOT NULL |
defaultValue changed |
SET / DROP DEFAULT |
enumValues updated |
DROP CONSTRAINT + ADD CONSTRAINT CHECK |
| Field removed | DROP COLUMN ... CASCADE |
| Entity removed | DROP TABLE ... CASCADE |
Changes are applied in a safe order: drop constraints, alter types, add columns, nullability, defaults, add constraints, drop columns.
id, created_at, and updated_at are protected.