Skip to content
MintGuides

Build

Models

Describe the things your project keeps — customers, orders, bookings — and get a table, form, filters and detail page for each.

Open Models
On this page

What a model is

A kind of thing your project keeps — customers, products, bookings, invoices.

A model is a list of fields. Creating one gives your project everything around it at once: a page in the sidebar with a table, search and filters, an add and edit form, a page for each record, a place on the dashboard builder, a permission for roles, and — if you want it — a public API. Nothing to deploy; it’s live as soon as it’s created.

Models live under Build → Models and need the Build permission. They belong to the open project only.

Creating a model

New model opens three short steps. Nothing is created until you press Create on the last one.

  1. What it stores — a title (like “Customers”) and the fields every record holds. The form people will fill in is drawn beside them as you type. More options holds the rest, which most models never need: the model name and address, record numbers, and who sees each record.
  2. Check the pages — the pages made from your fields, one part at a time: an overview, then the table page, form, record page, filters, fields & rules and — optional — Linked Relationships (rules that change the records this one picks, like a transfer taking money from one account and giving it to another). Next takes you through each in turn (the bar shows “part 2 of 7”), and Preview shows how they’ll look. They’re ready as they are; change anything on the way, with the same tools as Pages. Finish opens once you’ve seen every part.
  3. Finish — where it goes in the sidebar, a summary in plain words, and Create. Pick a sidebar section, or + New section… at the end of the list to name a new one — it’s added to the sidebar and picked. Then add the first record straight away, or open the table.

Going back and adding a field adds it to the pages too; your other changes stay. Progress is kept in your browser, so a reload carries on where you were — Start over clears it.

Rather describe it? Connect your own AI — Claude, ChatGPT and others can plan and build models in your project from a conversation.

Names and addresses

From a title like “Invoices” the builder makes the name Invoice and the address invoices — the page’s address in the panel and the model’s in the public API. Names belong to your project alone: other projects, and the platform itself, can have a Client or an Invoice without touching yours. Only if this project already has a model by that name is a number added (Invoice2), shown before you save. A few addresses are the project’s own — customers is your site’s signed-in customers — so a Customer model keeps its name and gets the address customers2.

The name and address are fixed once the model exists. The title can change any time.

Single types

For something there is exactly one of — the site’s settings, an About page, a footer.

Under a model’s Settings → How many records, pick Just one. Its page then opens that one record instead of a table — or, before it’s filled in, a Fill it in button. A second record can’t be added, from the form, a bulk upload or the API.

Its API answers with the record itself — GET /site-settings returns { "siteName": … }, not a list. A model with records can become a single type only while it has one at most.

Record numbers

Give every record a number (code), under the model’s Settings → Record numbers, numbers records as they’re created: a prefix, a dash and a padded number, like INV-0001. Two records created at the same moment never share one. Turning numbers on for a model that has records gives them numbers, oldest first; a new prefix applies from then on.

Switching it on fills in a prefix from the title (Invoices → INV); change it, or clear it for plain numbers (0001). Next record gets beside it shows exactly what the next record gets.

Fields

Each field has a name, a type, and whether it’s required.

Add a field first asks what it will hold — text, a number, a date, a choice from a list, an upload, a link to another record — each with a line on what it’s for. Type in the search box at the top to narrow the list (Enter picks the best match); then you name it. The type can be changed later in the field’s row, and its picture at the start of the row shows the type at a glance.

Each field is one line: its name, its type and whether it’s required. Under the name, in small grey letters, is its API name (the key): how your API, imports and formulas refer to the field. It’s made from the name until you change it. Beside it is what the type needs — the model a link points to, a formula, a section’s fields — and a short summary of the rest, like default: draft or no duplicates.

Click a field to open the rest of its settings. The open field is raised and edged in blue so it’s easy to tell apart; Done or Esc puts it away. Its settings come in groups: In the form (what it starts with — the default — and help text), Limits or Length (lowest/highest, fewest/most characters), the options with their labels, Table and search (a column in the table, found by the search box, no duplicates, faster sorting and filtering) and the API name.

The ⋯ at the end of a field duplicates it, adds a new field right below it, moves it up or down, or removes it. You can also drag it by ⋮⋮.

Beside the list, Form preview draws the add form from your fields as you edit them — before you save. The open field is marked there too, and clicking a field in the preview opens it in the list.

KindHolds · shows as
TextA line of text · searchable, sortable
Long text / Rich textParagraphs / formatted text with headings, lists and links
EmailAn email address, checked and lower-cased
LinkA web address, checked · opens in a new tab
NumberA number, with optional min/max
Formula (number)A number worked out from other fields (see below)
Formula (date)A date worked out from other fields — a due date 30 days on, a renewal’s new expiry (see below)
Yes / NoA switch · a Yes/No filter. Left unticked it saves as No — a real answer, so Required never stops the save
DateA date; the default can be “when created”
ColorA colour, with a picker
OptionsOne of the values you list (a dropdown) — or several, with “Allow several”
TagsA list of words, free or from allowed values
Image / ImagesOne uploaded picture / a gallery · from your Media
File / FilesUploaded documents · shown on the record page in a section of their own, as icon tiles that open in a new tab
VideoAn uploaded video
Link to a record / recordsOne or more records of another model — a booking’s guest, a post’s tags
Section / Section listA group of fields, or rows of them (see below)
PasswordA login or key you keep for someone — shown as dots until you click the eye (see below)
SEOA record’s search and share details in one editor — Google and share previews, meta title and description, Open Graph and X cards, indexing, structured data, sitemap. For pages, posts, products · see A page’s SEO
  • Allowed values: the only values the field takes; the form offers them as a list. Anything else is refused.
  • Starts with (default): what a new record starts with — prefilled in the form, and used when your API call leaves it out.
  • Some keys are reserved: _id, code, createdAt, updatedAt, customer, and secret-looking names like password or token. Every model gets createdAt and updatedAt on its own.

Address (slug) fields

A record’s address on your site, made from its title: “My first post” → my-first-post.

Add a field of the kind Address (slug) and pick what it’s made from — usually the title. While you add a record, the address follows the title as you type; type your own to change it. Saved addresses don’t move when the title changes later, so links to them keep working.

Always uniqueTwo records can’t share an address. One that’s taken — made from the title or typed by you — gets -2, -3… (local-chicken, local-chicken-2).
Left emptyIt’s made from the field you picked when you save — Local Chicken → local-chicken.
TidiedLower case, words joined by dashes, accents dropped (Café → cafe). Bangla and other scripts are kept.
On the APIFilter by it to find a record by its address: ?slug=my-first-post.

Password fields

For a credential you keep on a record — a client’s portal login, a Wi-Fi key, a supplier account.

A field of the kind Password shows as dots in tables, on record pages and in the form. Click the eye to see it; the copy button copies it without showing it.

  • It’s hidden on screen only: anyone who can see the record — in the panel, an export or the API — can read it. Keep such models to the people who need them (roles and record access).
  • History says the password changed, never what it was or is.
  • It can’t be unique, searched, sorted or filtered, and has no default. A key that says what it holds (password, pin, token) needs this kind — any other kind refuses it.
  • Not for your app’s own sign-in passwords: your site’s customers sign in through the public API’s customer accounts.

Sections and lists

A Section groups fields under one key — an address with street, city and postcode. A Section list is rows of the same fields, as many as needed — an invoice’s lines, each with an item, quantity, rate and total. Choosing either opens its own field editor, starting from an example you can change; the row’s “5 fields: …” button opens it again. Inside, fields can be text, long text, email, link, colour, number, formula, yes/no, date, options, image or file.

Components

A group of fields made once and used by any model — a button, an SEO block, a card.

Build → Components → New component: give it a title and its fields. Its address — shared.button, seo.meta — is made from a category and a name and stays fixed. A component’s fields can be text, rich text, pictures, files, choices, numbers, dates and the rest, a group or list of rows, links to records, and other components (see below).

To use one, add a field of the kind Component to a model and pick it — One (a page’s main button) or A list (its row of buttons, with at least / at most under More). The form shows its fields together under the field’s name; the API sends it as an object, or a list of them.

Change it onceEvery model using it follows: its form, its record page and its API.
Left blankA component nobody filled in is simply empty — its own required fields only count once something is typed in it.
DeletingNot while a model or another component uses it — the message names where. Take it off those fields first.

Links and components inside a component

A component can link to records — a Featured product with a Product field (Link to a record) and Also try (Link to records). The form gets the same record picker as anywhere else; the record page and the API give the linked records’ names.

A component can also hold another one — a Call to action inside the Featured product, once or as a list. Only one level deep: the inner component’s fields must all be plain (no groups or components of their own), and a component that sits inside another stays that way. Changing the inner one reaches every model using the outer one.

Image fields with details

An Image or Images field stores the picture’s address. Turn on Send each picture with its details (in the field’s options, under On the API) and the API answers with the media library’s details instead: { "url": "…", "alt": "…", "caption": "…", "width": 1600, "height": 900, "focal": { "x": 0.33, "y": 0.7 } }. It works inside sections, components and content blocks too. A picture that isn’t in the library comes with empty details. Set the details in Media.

Make a section reusable

Made a Section or Section list in one model and want it in another? On the model’s Fields tab, Make reusable under it turns it into a component — give it a title and an address. The field then uses that component (once, or as a list, as before), its records stay exactly as they were, and any other model can pick it. Save other changes to the model first — it’s made from the model as saved.

Content blocks

A field of components in any order — a page’s hero, text, questions, a button.

Add a field of the kind Content blocks and pick the components it takes (they’re made under Components). Whoever fills in a record then builds it from those: Add offers each one, every block opens to its own fields, and the arrows move it up or down; copy and remove are beside them. Under More, How many sets at least / at most.

Each block is checked as its component — its required fields, its options — and keeps a _key however it moves. The API sends them in order: [{ "__component": "content.hero", "_key": "4uh57zhf", "heading": "…" }, …]. Changing a component changes its blocks in every model that takes it.

Calculated fields

A Formula (number) field works itself out from other number fields.

The formula sits at the bottom of the dialog, locked — it’s built by clicking, not typed. The line on top says what to pick next, and whatever you pick goes into the highlighted box: a field from Fields, a number or text from Values, + − × ÷ or Round from Calculate, or If from Decide. Pick If and it asks for the condition — pick Discount type and choose Percentage from its values — then the value, then the value otherwise; Otherwise if checks another condition first. Right after a field or number, + − × ÷ keep calculating with it. Click any part of the formula to change it (click the sign between two parts to work on both); Remove or Backspace empties it, and Undo takes a step back. It can’t be saved while a box is empty. Type it yourself unlocks it to type by hand — the buttons then go in at the cursor — and Back to buttons locks it again.

Build it from the field list, the operators (+ − × ÷ %, brackets) and functions (round, floor, ceil, abs, min, max), or type it: total - paid, round(price * qty * 1.05, 2). Over a section list, sum(items.total), avg(items.total) and count(items); a formula inside a row uses that row’s values (quantity * rate). Try it works it out on sample numbers. Use a field as many times as you need — price * qty + price.

Conditions pick between two calculations with if(condition, then, otherwise). A discount that is either flat or a percentage, with a Discount type choice field: subtotal - if(discountType = "percentage", subtotal * discount / 100, flatDiscount). Compare with = != < <= > >=, join conditions with and / or, turn one round with not(…), and put text in quotes. Text is compared ignoring capitals and spaces at the ends. A choice, text or yes / no field can be compared, or used as the condition itself — if(isVip, 5, 0) holds when it’s ticked. Put an if inside another for more than two cases: if(tier = "gold", 20, if(tier = "silver", 10, 0)). Only the part the condition picks is worked out, so if(qty > 0, total / qty, 0) never divides by 0.

Through a link to a record, a formula reads that record’s numbers: with a Tax link, price * qty + tax.amount adds the amount of the Tax picked. The field list offers them as Tax › Amount — in the model builder, and in a page’s Fields & rules tab too. No Tax picked counts as 0. When someone changes the Tax’s amount, every record that picks it is recalculated straight away; if the Tax is deleted, it counts as 0 for them.

A formula field can’t be typed into: forms show the result as you type, and the server calculates it on every save — from the panel and from your API alike. Changing a formula recalculates existing records. Empty counts as 0; dividing by 0 leaves it empty.

A number formula can count days between two dates: daysBetween(today(), expiresAt) gives the days left on a membership. For a date itself, use a Formula (date) — a type of its own.

Formula (date)

A Formula (date) field works out a date: a due date, an expiry, a renewal. It’s a type of its own, beside Formula (number).

It’s built the same way as a formula, with a Dates row of buttons: Today, Add days, Add weeks, Add months, Add years, Add… (the unit from a field), Latest of and Earliest of. Some examples:

  • addDays(invoiceDate, 30) — due 30 days after the invoice date. A number below 0 goes back in time.
  • addMonths(joinedAt, plan.months) — a month count read from the plan the record links to. 31 January plus one month is the end of February.
  • add(startDate, plan.length, plan.unit) — when the plan says how long in a field (30) and in what (days, weeks, months or years) in another.
  • latest(member.expiresAt, today()) — the later of the two; an empty date is left out.
  • if(status = "paid", addYears(paidOn, 1), addDays(today(), 7)) — one date or another, by a condition. Dates can be compared too: expiresAt < today().

Work it out, under the formula, says when:

ChoiceWhat it does
Every time the record is savedLike a formula: it follows the fields it reads, and moves when they change.
Once, when the record is addedWorked out when the record is added, then kept. Records that were there before the field get theirs when you save the model. Use it with today() or a linked record’s date, which keep changing.

A date it needs that’s empty leaves the Formula (date) empty. Dates are worked out in UTC.

A membership renewal

A gym wants a renewal to add the plan’s length after the current expiry, or after today if the membership has already run out:

  • Plans: Name, Price, Length (a number) and Unit (options: days, months).
  • Members: Name, Plan (a link), Fee and Expires at (a date).
  • Renewals: Member and Plan (links), and Renewed until, a Formula (date) worked out once: add(latest(member.expiresAt, today()), plan.length, plan.unit).
  • On the Renewals page, a Linked Relationship when a renewal is added: change the Member — Expires at Copy from Renewed until, Fee Copy from Plan › Price.

A member who expires on 30 November and renews on 19 October for a month runs to 30 December; one who expired in September runs to a month from today. Deleting the renewal puts the old expiry back.

Autofill

Autofill is a setting on a field: it copies a field of a record picked in another field of the same form — an order’s customer phone and email.

Add a Link to a record field first (the customer). Then open any text, number, date, Yes/No, options or link field, and under Autofill turn on Fill this from a linked record. Choose the link it copies from and what to take: one of the linked record’s fields (Phone), or a formula over its numbers (total - paid). The field stays what it is — a Phone stays a Phone — and shows, sorts and filters like it.

SettingWhat it does
Fill it withThe field of the picked record to copy, or a formula over its numbers.
Only whenFill only while conditions hold — a field of this record (Delivery is Home) or of the picked record (Customer › Type is Wholesale). With none, it always fills.
Read-onlyOff (default): filled when you pick the record, and you can type over it for this record. On: nobody types it — the server sets it and keeps it in step with the record.
What happens
On the formFilled in the moment you pick the record. You can type over it for this record (unless it is read-only); picking another record fills it again, replacing what was typed. Clearing the pick keeps what was typed, and empties a read-only one.
When the source record changesAn editable autofill: nothing — it is your copy now. A read-only one follows — change the customer’s email and every order that copies it shows the new one.
When “Only when” stops holdingAn editable autofill is simply left alone. A read-only one is cleared and stays locked, and fills again when the conditions hold.
Saved through the APIAn editable autofill is filled from the record when you leave it out. A read-only one is always set from the record; a value sent for it is ignored.

In a list’s rows an autofill works per row: a Product picked in an invoice item fills its Rate and Unit (rows of a Section list can hold links to records and autofill fields). A read-only number can be used in formulas like any number — quantity * rate.

A hidden field (a password, a token) can’t be copied. An autofill takes one hop: the picked record’s own fields, not a field of a record it links to. A number fills a number field, a date a date, and text fills a text field — pick a field that fits.

Who sees the records

Your role and your projects decide — and, where a model needs it, each record.

In your organization, a model’s records are open to everyone who can open the project and whose role has Records: View (Add, Edit and Delete likewise). To keep people to some work, give them only the projects it’s in — see Which projects people open.

For confidential records — salaries, contracts, personal notes — switch on Let each record choose who can see it, under the model’s Settings → Who sees each record. Every record then has an owner (whoever created it) and a privacy, chosen in the form’s Manage access section:

PrivacyWho sees the record
Only meIts owner alone.
PrivateIts owner and the people they add — anyone in the organization who can open this project.
PublicEveryone who can view the model’s records.

Only the owner can change who has access, or delete the record. The table gains Privacy and Owner columns and filters. Records that existed before you switched it on become Public, so nobody loses sight of them. Your public API only ever reaches records marked Public, and anything your site sends in is Public.

For the people using your site or app it’s different: a public model can be set to customers’ own records only, so each customer sees just what they created.

Sign-in

Let people sign up and sign in to your site or app as one of a model’s records.

On a model’s Advanced tab, switch on People can sign up and sign in. The model can then hold the customers of your Login widget: pick it in the widget’s setup. Models with the switch off aren’t offered there.

  • A project has one sign-in model. To tell kinds of customers apart (customer, seller, picker), give it an Options field such as Customer role — see Customer roles.
  • The model needs an Email (or Text) field, because people sign in with it.
  • It must be a list. A single type holds one record, so it can’t hold accounts.
  • Passwords are never stored in the records. MINT keeps them safely, apart from your data.
  • While the Login widget uses the model, the switch can’t be turned off. Choose another model, or MINT’s Customers, in the widget first.

Drafts and publishing

Edit as much as you like; your site and API change when you press Publish.

Turn on Records are drafts until published in a model’s Settings (Drafts and publishing). Its records are then drafts: saving changes what you see in the panel, but your site, the public API and its filters, sorting and search keep using what was last published. Turning it on publishes the records already there, so nothing leaves your site.

Each record’s page says where it stands, beside its title, with the buttons that move it:

It saysWhat it means
DraftNever published — not on the site or API. Publish puts it there.
PublishedLive, and the same as what you see.
Changes not publishedLive, but edited since. Publish puts the changes live; Discard changes brings the record back to what’s published.

Take off removes a record from the site and API and keeps it as a draft. Links to other records, numbers and History stay as they are whatever you do.

All content, straight after the Dashboard in the sidebar, gathers it: the website’s Contents, your single types, and every model with drafts — how many records each has, how many aren’t live, what changed last. In a model’s table, the Status column says where each record stands and its filter shows just the drafts or the changed ones; tick rows and Menu → Publish puts them live together. A record’s page says when it was last published and by whom. In any form, ⌘S (Ctrl+S) saves.

Publishing is a right of its own: Publish on a role (the standard roles have it), or ticked per model on a project’s own role. Someone who can edit but not publish writes drafts, and the record page tells them someone who can publish puts it live. Turning drafts on for a model gives Publish to the project roles that could edit it, so nobody loses what they could do.

Scheduling

Set a record to go live at a time, to come off at a time, or both.

On a record’s page, Schedule (beside Publish) asks for two times, in your own time zone:

TimeWhat happens then
Publish atThe draft as it is at that time goes live — edits you make before then go with it.
Take off atIt leaves your site and API. The draft stays in the panel. It has to be after Publish at, and needs the record live or a publish before it.

Either one can be left empty; the × beside a time clears it. Once set, the button says what’s next (“Publishes 20 Oct, 09:00”) and the record’s page says it under the title. All content lists what’s coming up across the project, soonest first. Times can be up to two years ahead.

It happens within a minute of the time, as the person who set it: History shows “published … as scheduled”, the project’s publish and unpublish webhooks go out (their source is schedule), the site refreshes, and the person who set it gets a notification. If it can’t happen — drafts were turned off on the model, say — they’re told why instead. A record deleted before its time simply isn’t published.

Scheduling needs the Publish right, like Publish itself. Your connected AI can schedule too — “publish it on Monday at 9” — when its key can publish.

Changing a model

Open it from Build → Models. Changes apply as soon as you save.

A model’s page has four tabs:

TabWhat’s there
FieldsThe fields, in order, and the form preview beside them. Page layout opens the table, form and record page designer.
SettingsBasics (title, record name, sidebar, description), Record numbers and Who sees each record.
ConnectionsWhat this model links to, and which models link to it.
AdvancedThe fixed names (model name, address, collection, version), and Turn off or delete.

Edits are kept until you click Save changes — in the header, in the bar that appears at the bottom while something is unsaved, or with ⌘S / Ctrl+S. Undo all goes back to the saved model. A tab with a red number has fields that need attention.

  • Adding a field is always safe; it appears in the table, form, detail page and filters straight away.
  • Removing a field hides it: the values stay in the records but aren’t shown or returned. Add it back to see them.
  • Changing a field’s kind is flagged: old values stay as they were and may not read correctly as the new kind.
  • Making a field unique fails if records already share a value — you’re told which.
  • Every change is kept as a version before it’s applied.

Turning off and deleting

Both are on the Advanced tab. Turn off takes the page away but keeps the model and its records. Delete removes the model, its page and its sidebar entry; its records stay unless you tick “Also delete its records” and type the model’s name.

Features built by AI

Several linked models at once — a whole feature from one description.

When your connected AI builds a feature — say “bookings with guests, rooms and payments” — it makes the models, the links between them and the tabs on their pages in one go, all or nothing. Models → Features lists every feature built, with links to what it made. Everything it made can be changed afterwards like any other model.

Troubleshooting

SymptomWhy, and what to do
There’s no Build sectionYour role lacks the Build permission.
The name got a 2 on the endThe project already has a model by that name. Change the title, or keep it.
“Isn’t a number field” in a formulaFormulas only use number (and formula) fields.
“Works out a date” in a Formula (number)A Formula (number) holds a number. Make the field a Formula (date), or use daysBetween(…) for a number of days.
“Not with + − × ÷” in a Formula (date)Dates move with Add days, Add months… — addDays(start, 30), not start + 30.
Making a field unique failedSome records share a value. Fix them, then try again.
I can’t delete a modelAnother model links to it. Remove that link field first.
A teammate doesn’t see the new pageTheir role needs Records: View, and the project must be one of theirs.