Appbricx
Docs
FeaturesPricingStart free

Get started

OverviewWhat Appbricx isQuickstart with a coding agentQuickstart with the builderCore concepts

Guides

Data and tablesAccess and rolesBusiness rulesSign-inCustom screensRealtime and topicsWorkflows, schedules and webhooksServer functionsAI in your appPaymentsFilesMessagesIntegrationsVerifyPublishing, domains and rollbackOperating your appExport and self-hostSelf-host install

Tutorials

Build a booking app with Claude CodeMulti-user todo listClient portalSlack on new sign-upTemplates

Reference

Reference homeFrontend SDKHTTP APIMCP toolsCLIIntegrations catalogContract formatAI key resolutionRun Appbricx locally

Describe your app in one file

Your whole app lives in one JSON document called the contract: the kinds of users it has, the data it stores, who can see and change which rows, the rules the server enforces, background workflows, screens, and sign-in. Your agent writes it. Appbricx builds the database, access policies, sign-in, workflows and UI from it.

Why it matters to you

  • Your app is easy to change. Adding a field or a role is a small edit to one file, not a hunt through code.
  • Your work carries over.The contract stays in the project, so the next session (yours, a teammate's, or the built-in builder) reads it back with get_contract and continues.
  • Mistakes are caught before they ship. Appbricx checks the contract, fixes what it safely can and tells you exactly what it changed.

You rarely write the contract by hand. Read this page when you want to understand what your agent wrote, or to steer it precisely.

Full reference. The guide served by get_guide (MCP), appbricx docs (CLI) and GET /dev/v1/docsis generated from the platform's own source and is always current. This page is the overview.

Top-level keys

KeyRequiredWhat it holds
appYesname, tagline; optional brand_icon, theme, nav_style (sidebar | topbar | rail), density (comfortable | compact), direction, brand_color (a hex colour)
personasRecommendedThe roles: id, label, description, kind (staff | consumer)
entitiesYes (at least one)Tables, their fields, access, owner refs and rules
seedsNoRows per entity
pagesNoYour own pages. Omit it and every table gets a list + form page for its staff roles
workflowsNoUp to 10 server-side workflows (on data change, schedule or webhook)
componentsNoCustom React components with their source. See Custom components
authNoSign-in screen presentation and providers, e.g. { "screen": "MyLogin" } for your own sign-in component, or { "google": true } to turn on Sign in with Google. See Sign-in methods
demo_loginsNoDemo sign-ins: [{ "persona", "row", "email"?, "password"?, "name"? }], up to 8

Personas (roles)

  • staff personas manage shared records. consumer personas act through pages (forms, funnels, composers).
  • The first consumer persona is the public sign-up. With no consumer persona the app is invite-only, and the first staff role gets a Team & access page to add people.
  • A person's own row lives in the entity named exactly like their persona id (the technician persona → the technician table).

Entities

Names are snake_case and singular. Every table already has id, created_at, updated_at, user_id, org_id and created_by. Never declare them.

KeyMeaning
nameTable name
label_fieldThe field that names a row in the UI
fields{ name, type, enum_values?, ref?, optional? }. Types: text, number, boolean, date, timestamp, enum, ref, email, url, image, attachment. A ref names another entity
uniqueField combinations that must be unique, e.g. [["slot", "date"]]
accessPer-persona overrides. read / update / delete: "own" | "all" | "none"; create: true | false
owner_refsRef fields that assign a row to a person. A row is "own" when any listed ref points at the viewer's row (transitively). [] = creator only
actor_refA ref that always names the writer's own persona row (e.g. sender_id). The server stamps it on every write by a non-staff role
rulesBefore-write checks, up to 6. See Before-write rules

Access is derived from personas and pages and enforced in Postgres with row-level security. Only add access where the default is wrong. verify then proves it with a role × table probe.

Seeds, pages and workflows

  • Seeds. Seed ids like "t1" are labels for refs between seed rows; the platform turns them into stable UUIDs. After the first apply, omit seeds to keep every row.
  • Pages. Each page has a name, route, title, personas and a layout tree of blocks and custom component nodes. "chrome": "none" makes it full screen. See get_guide sections pages and blocks.
  • Workflows. { id, title, on, when?, do[] }. Triggers: an entity event, a schedule or a webhook. Actions include notify, email, integration and (last resort) custom code. Omitting workflows keeps the current ones; a present list replaces them. Workflows run after a write; use rules to stop one.
  • Demo sign-ins. Every persona gets one login, linked to the first seed row of its table and named after that person: ava@tablefor.test with a word password like maple-otter-river-27. Add demo_logins when a demo needs two people (chat, dating, a marketplace). To choose the credentials yourself, give an entry email, password (8+ characters) and/or name. They are used exactly as written; on the first seed row they replace that persona's default login. Top-level accounts ({ email, password, role }) work too.

Example

A field-service app: dispatchers create and assign jobs, technicians see and update only their own. This is the example contract that ships with the Claude Code skill.

{
  "app": { "name": "Field Desk", "tagline": "Jobs, sites and the people who fix them" },
  "personas": [
    { "id": "dispatcher", "label": "Dispatcher", "description": "creates and assigns jobs", "kind": "staff" },
    { "id": "technician", "label": "Technician", "description": "works the jobs assigned to them", "kind": "staff" }
  ],
  "entities": [
    {
      "name": "technician",
      "label_field": "name",
      "fields": [
        { "name": "name", "type": "text" },
        { "name": "email", "type": "email" },
        { "name": "phone", "type": "text", "optional": true }
      ]
    },
    {
      "name": "site",
      "label_field": "name",
      "fields": [
        { "name": "name", "type": "text" },
        { "name": "address", "type": "text" }
      ]
    },
    {
      "name": "job",
      "label_field": "title",
      "fields": [
        { "name": "title", "type": "text" },
        { "name": "site", "type": "ref", "ref": "site" },
        { "name": "technician", "type": "ref", "ref": "technician", "optional": true },
        { "name": "status", "type": "enum", "enum_values": ["open", "assigned", "done"] },
        { "name": "due", "type": "date", "optional": true }
      ],
      "owner_refs": ["technician"],
      "access": { "technician": { "read": "own", "update": "own", "delete": "none" } }
    }
  ],
  "seeds": {
    "technician": [
      { "id": "t1", "name": "Asha Rao", "email": "asha@example.com" },
      { "id": "t2", "name": "Ben Ortiz", "email": "ben@example.com" }
    ],
    "site": [
      { "id": "s1", "name": "Harbor Mall", "address": "1 Quay St" },
      { "id": "s2", "name": "Northside Clinic", "address": "40 Elm Ave" }
    ],
    "job": [
      { "id": "j1", "title": "Boiler pressure drop", "site": "s1", "technician": "t1", "status": "assigned", "due": "2026-10-10" },
      { "id": "j2", "title": "Replace door closer", "site": "s2", "status": "open" }
    ]
  },
  "workflows": [
    {
      "id": "job-assigned",
      "title": "Tell the technician about a new assignment",
      "on": { "entity": "job", "event": "updated" },
      "when": { "field": "status", "equals": "assigned" },
      "do": [ { "action": "notify", "to": "technician", "role": "technician", "title": "New job: {{title}}", "body": "Due {{due}}" } ]
    },
    {
      "id": "overdue-digest",
      "title": "Morning digest of new jobs",
      "on": { "schedule": "daily 08:00", "entity": "job" },
      "do": [ { "action": "notify", "role": "dispatcher", "title": "{{count}} new job(s) since yesterday", "body": "{{items}}" } ]
    }
  ]
}

No pages: each table gets a management page for its staff roles. "to": "technician"sends the notification to the person the job's technician ref points at, not to the whole role.

The check report

check_contract (and every apply and patch) returns a report:

FieldMeaning
okTrue when there are no errors
errorsWhy the contract can't be built. Must be empty to apply
fixesWhat the normalizer matched, filled or dropped. Make each one explicit in the contract so your intent is clear
warningsBuilt fine but worth a look (e.g. pages nobody can reach)
summaryapp, roles, entities (fields, seed row counts), pages (name, route, personas), workflows(id, trigger, actions). Null when the contract doesn't parse

An apply with errors is refused with 422, code: "CONTRACT_INVALID" and the report. Nothing is half-built.

Patch ops

After the first apply, send small typed edits with patch_contract instead of resending the whole contract. Each op is validated, then the whole contract is re-normalized and rebuilt. dry_run reports without building. Up to 50 ops per call.

OpFieldsNotes
add_entityname, fields[], title?, personas?Also adds its management page
add_fieldentity, fieldAdded fields are optional
update_fieldentity, name, enum_values?, optional?Enum options can be added, not removed while in use
remove_fieldentity, nameLeaves the app; the column and its data stay
set_workflowworkflowAdd or replace by id (kebab-case)
remove_workflowid
add_pagepage (name, route, title, …)The route must be new
set_pagepage, title?, icon?, personas?, layout?, form?, chrome?Route and name stay fixed
remove_pagepage
set_componentcomponentAdd or replace by name; new source is gated on apply
remove_componentnamePages that placed it drop the node
edit_componentname, edits[{ find, replace }]Each find must occur exactly once
set_entityentity, access?, owner_refs?, actor_ref?, label_field?, rules?A present key replaces; null removes
set_apptheme?, direction?, nav_style?, density?, brand_color?, auth?brand_color: "" removes it; auth: null removes the auth section
set_demo_loginslogins[{ persona, row, email?, password?, name? }]Replaces the demo sign-ins (max 8)

Example

[
  { "op": "add_field", "entity": "job", "field": { "name": "priority", "type": "enum", "enum_values": ["low", "normal", "urgent"] } },
  { "op": "update_field", "entity": "job", "name": "status", "enum_values": ["open", "assigned", "done", "cancelled"] },
  { "op": "set_app", "brand_color": "#0F766E" }
]

Send it as edits to patch_contract, as the body { "edits": [...] } to POST /dev/v1/projects/:id/patch, or save it as a file and run appbricx patch ops.json.

Next

  • Add your own screens and brand
  • Enforce your business rules
  • Connect any MCP client
Appbricx

The backend, runtime and hosting for apps your coding agent builds. On our cloud, in yours, or on your own servers.

Features

Coding agents and MCPVerify testingBuilder consolePostgres databaseData APIRealtimeRow-level securitySign-in and authPaymentsWorkflows and jobsAI in your appIntegrationsBackups and rollbackHosting and domainsSelf-host and exportAll features

Product

ShowcasePricingFor coding agentsTemplatesPrivate cloudBring your own AI keyFAQ

Compare

Appbricx vs SupabaseLovable alternativeBolt alternativeFor freelancersFor agencies

Resources

DocumentationReferenceBlogSupportContact

© 2026 Appbricx. All rights reserved.

TermsPrivacyCookiesAcceptable Use