Tutorial: your first app

Build a small Invoices app end to end — collection, fields, picklist, layout, list view and navigation — in the console, then the same thing from the CLI and from an MCP client.

This tutorial builds an Invoices app: a collection with a customer lookup, an amount, a due date and a status picklist, then the pieces that make it usable — a page layout, a kanban list view and a tab in the end-user app. You will do it three ways. Each way produces the same metadata, so pick whichever fits how you work.

You need a running Kelta (quickstart) and an administrator login.

What you will build#

ObjectPurpose
customers collectionone field, name — the target of the lookup
invoices collectionnumber, customer (lookup), amount (currency), due_date (date), status (picklist)
invoice-status global picklistDRAFT, SENT, PAID, OVERDUE
validation rulean amount must be positive
Main layouthow a single invoice is displayed
By status list viewa kanban board with one lane per status
Finance appa top-nav app with an Invoices tab

Part 1 — In the admin console#

1. Create the collections#

Open Setup → Data model → Collections → Create collection. The wizard has four steps: Basics (API name customers, display name Customers), Fields (add name, type Text, required), Authorization (tick Create/Read/Edit/Delete for the profiles that should use it) and Review. Click Create collection.

Repeat for invoices. On the Fields step add:

FieldTypeOptions
numberTextrequired, unique
customerLookuptarget collection customers, required
amountCurrencyrequired
due_dateDate

Leave status for the next step — it needs a picklist first.

Behind the scenes each collection gets a physical table, a /api/<name> endpoint and an entry in the gateway’s route table; you can already GET /api/invoices. See Collections.

2. Add the status picklist#

Open Setup → Data model → Picklists → Create picklist, name it invoice-status and add the values DRAFT, SENT, PAID, OVERDUE (mark DRAFT as default). Then, back in the invoices collection, add a field status of type Picklist and choose the invoice-status global picklist as its source.

3. Add a validation rule#

Open the invoices collection → Validation rules → New rule:

  • Name: Positive amount
  • Error condition: amount <= 0
  • Message: Amount must be greater than zero

The formula is an error condition: the record is rejected when it evaluates to TRUE. See Validation rules.

4. Lay out the record page#

Open Setup → Data model → Page layouts → Create layout for invoices, name it Main and mark it as the default. Without a layout the record page still renders — every field in one section — so this step is about control, not necessity. Put number and customer in the first section’s two columns, then amount, due_date and status in a second section named Billing. Columns are 0-based when you edit the JSON directly. See Page layouts.

5. Save a kanban list view#

Open Setup → Data model → List views → New list view for invoices: name it By status, choose the columns number, customer, amount, due_date, set the renderer to Kanban with status as the lane field, and set visibility to Public so everyone sees it. See List views.

6. Put it in an app#

Open Setup → UI customization → Menus → New menu, name it Finance and add an item Invoices with the path /resources/invoices. Mark the menu as default if it should be the first app users land in. See Apps and navigation menus.

7. Use it#

Switch to the end-user app (the app switcher in the header, or open /<tenant>/app/o/invoices). Create a customer, then an invoice; drag the invoice between lanes in the By status view. Try to save an invoice with a negative amount and read the validation error.

Part 2 — From the CLI#

Install and log in first (Install the CLI). Every command prints JSON when piped, so the same script works for a person and for CI.

kelta collections create --name customers --display-name Customers
kelta fields add customers --name name --type text --required

kelta collections create --name invoices --display-name Invoices
kelta fields add invoices --name number   --type text --required --unique
kelta fields add invoices --name customer --type lookup --reference customers --required
kelta fields add invoices --name amount   --type currency --required
kelta fields add invoices --name due_date --type date

kelta picklists create --name invoice-status --values DRAFT,SENT,PAID,OVERDUE
kelta fields add invoices --name status --type picklist --picklist invoice-status

kelta validation-rules create invoices \
  --name "Positive amount" \
  --formula "amount <= 0" \
  --message "Amount must be greater than zero"

Layouts and menus are applied as trees from a file — one idempotent call per object:

cat > invoices-layout.json <<'JSON'
{
  "name": "Main",
  "layoutType": "DETAIL",
  "isDefault": true,
  "headerConfig": { "titleFields": ["number"] },
  "sections": [
    { "heading": "Overview", "columns": 2,
      "fields": [ { "name": "number", "column": 0 }, { "name": "customer", "column": 1 } ] },
    { "heading": "Billing", "columns": 2,
      "fields": [ { "name": "amount", "column": 0 }, { "name": "due_date", "column": 1 }, { "name": "status", "column": 0 } ] }
  ]
}
JSON
kelta layouts apply invoices --file invoices-layout.json

kelta list-views apply invoices \
  --name "By status" \
  --columns number,customer,amount,due_date \
  --view-type KANBAN --lane-field status \
  --visibility PUBLIC

cat > finance-menu.json <<'JSON'
{
  "name": "Finance",
  "isDefault": true,
  "items": [ { "label": "Invoices", "path": "/resources/invoices" } ]
}
JSON
kelta menus apply --file finance-menu.json

Run the apply commands a second time: every one reports unchanged. That is the property that makes them safe to keep in a setup script or a CI job. See the CLI command reference.

Part 3 — From an MCP client#

Connect an MCP client to the tenant’s admin endpoint (Connecting) and ask it to build the app. The admin toolset follows the same build order the console does:

  1. create_collection (customers, then invoices with an inline initial field set)
  2. add_field for any field not created inline
  3. apply_picklist for invoice-status, then add_field for status referencing it
  4. create_validation_rule
  5. apply_layout — the same tree document as the CLI file above
  6. apply_listview — kanban on status
  7. apply_menu — the Finance app with its Invoices item

Every apply_* tool is create-or-update keyed on a natural name and reports {"action": "created" | "updated" | "unchanged", "id": ..., "changed": [...]}, so an agent can re-run its plan until it converges. Before authoring by hand, an agent should read the kelta://docs/page-layouts, kelta://docs/list-views and kelta://docs/ui-menus resources — they are the same documents as the authoring reference on this site.

Optional: a custom page#

The page builder lets you compose a page from widgets bound to your data. A minimal page that lists invoices:

{
  "schemaVersion": 2,
  "dataSources": [ { "name": "invoices", "collection": "invoices", "mode": "list", "limit": 50 } ],
  "components": [
    { "id": "h1", "type": "heading", "props": { "text": "Open invoices", "level": "h2" } },
    { "id": "t1", "type": "table", "props": { "source": { "$bind": "data.invoices" } } }
  ]
}

Create it in Setup → UI customization → Pages (or kelta pages apply page.json then kelta pages publish /open-invoices) and add /p/open-invoices to the Finance menu. See Page builder.

Where next#

Edit this page on GitHub