LumiDevKit schema format for AI

This is the complete specification for the JSON that LumiDevKit's Database Visualizer reads. It is written to be handed to an AI: paste the prompt below into ChatGPT or Claude along with a description of your app, and you get back a file that loads correctly the first time.

Read this before anything else. The parser never rejects a bad field type. Every unrecognised type string is silently accepted and turned into a meaningless plain value. There is no error, no warning, and no red text — you just get a diagram that is quietly wrong. One example makes the stakes clear: write list.user instead of list.custom.user and the diagram labels the field user[], which looks exactly right, while drawing no relationship line at all.

That is why the prompt below is so specific, and why this page spends more words on failure modes than on the happy path.

Hand this to ChatGPT or Claude

Copy it, paste it into a new chat, replace the last line with a description of your app, then save the reply as my-app.json and upload it to the Database Visualizer.

Or point an AI straight at it — no copying:

The file

A LumiDevKit schema is a JSON object with exactly two top-level keys:

{
  "user_types": {},
  "option_sets": {}
}

That file is valid. It loads, and shows you an empty diagram containing only Bubble's built-in User.

  • user_types holds your data types — the equivalent of tables.
  • option_sets holds your fixed choice lists — the equivalent of enums.

Any other top-level key is ignored. That is deliberate, and it is why you can drop a complete Bubble application export straight onto the Database Visualizer: it simply picks out these two keys and discards pages, element_definitions, api, styles, settings and the rest.

Required and optional keys

KeyRequiredWhat happens if it's wrong
user_typesYes — must be an object, not null, not an arrayMissing or null: file rejected, "No database types found." An array slips past that check and loads without an error
option_setsNo — defaults to {}Nothing; the diagram just has no option sets
user_types.<key>.displayYes in practiceThe box renders labelled undefined. There is no fallback
user_types.<key>.fieldsNoThe type appears with no fields
option_sets.<key>.displayNoFalls back to the key, with _ turned into spaces
option_sets.<key>.valuesNoAn empty option set, with no warning
deleted: trueNoThe item is hidden from the diagram by default

The display asymmetry catches almost everyone: option sets fall back to their key, data types do not. A data type without display is not an error — it is a nameless box.

The complete value grammar

Every field is { "display": "...", "value": "..." }. The value string is matched against the list below, and the first match wins.

<!-- GRAMMAR:START -->
valueMeansDraws a link?Treated as a list?
textFree text: names, emails, URLs, phone numbers, long textNoNo
numberIntegers, decimals, money, quantities, countsNoNo
dateA date and timeNoNo
booleanBubble's yes / noNoNo
imageAn uploaded imageNoNo
fileAn uploaded non-image fileNoNo
geographic_addressA physical address or map locationNoNo
list.textA list of text valuesNoYes
list.numberA list of numbersNoYes
list.dateA list of datesNoYes
list.booleanA list of yes / no valuesNoYes
list.imageA list of imagesNoYes
list.fileA list of filesNoYes
userOne Bubble UserYes, one-to-oneNo
custom.<type_key>One record of another data typeYes, one-to-oneNo
list.custom.<type_key>Many records of another data typeYes, one-to-manyYes
option.<option_set_key>One option from an option setYes, one-to-oneNo
list.option.<option_set_key>Many options from an option setYes, one-to-manyYes
<!-- GRAMMAR:END -->

That table is exhaustive. Anything not on it is accepted without complaint and rendered as a meaningless plain value.

Types LumiDevKit accepts but does not model

These are real strings you will find in genuine Bubble exports, or that LumiDevKit's own "add field" menu produces. They load without error, but the diagram cannot represent them:

You writeThe diagram showsWhat actually happens
list.useruser[]No link, and not treated as a list. Use list.custom.user
list.geographic_addressgeographic_address[]Not treated as a list. There is no list form of this type
date_rangedate_rangeA plain value with no meaning. Use two date fields
numeric_rangenumeric_rangeA plain value with no meaning. Use two number fields
date_intervaldate_intervalA plain value with no meaning. Use a number
Text, varchar, uuid, jsonbThe word you wroteA plain value. Type names are lowercase, from the table above

list.user is worth repeating because it is both the most common mistake and the hardest to spot: the label on the diagram ends in [], so it reads as a working list of Users. It is not. Real Bubble exports contain list.user, so an AI trained on Bubble data will reach for it.

Referencing other types

custom.X and option.X are matched against the literal JSON key of the target — never against its display.

{
  "user_types": {
    "order_item": {
      "display": "Order Item",
      "fields": {
        "product_custom_product": { "display": "Product", "value": "custom.product" }
      }
    },
    "product": { "display": "Product", "fields": {} }
  }
}

The link works because custom.product matches the key product. It would break with custom.Product, custom.products, or custom.Product Item.

The match is exact:

  • Case-sensitive. custom.Order does not find the key order.
  • Not pluralised. custom.orders does not find the key order.
  • Whitespace-sensitive. Keys should never contain spaces at all.
  • Dots are greedy. Everything after the first custom. is the key, so custom.a.b looks for a key literally named a.b. Do not put dots in keys.

A reference that points at a key you never defined does not raise an error. It draws a red [Missing: X] box on the diagram. Those red boxes are the fastest way to spot a broken schema, which is why the checklist below leads with them.

There is also no check that you used the right namespace: custom. pointing at an option set resolves happily, and so does option. pointing at a data type. Both produce a diagram that is wrong in a way nothing warns you about. Keep custom. for user_types and option. for option_sets.

Where this goes silently wrong

Every item here loads without an error message.

  1. list.user instead of list.custom.user — labelled user[], but no link and not a list.
  2. A reference to a key that does not exist — a red [Missing: X] box instead of a link.
  3. Referencing the display instead of the keycustom.Order Item rather than custom.order_item. Same red box.
  4. A capital letter in a referencecustom.Order. Same red box.
  5. A pluralised referencecustom.orders pointing at the key order. Same red box.
  6. A missing display on a data type — a box labelled undefined.
  7. custom. pointing at an option set (or option. at a data type) — a link that draws, styled as the wrong kind of thing.
  8. "options" instead of "values" in an option set — LumiDevKit reads both, but Bubble's own paste format expects values, so prefer it.
  9. deleted: true anywhere — the item vanishes from the diagram, because deleted items are hidden by default. Turn on Show deleted to see them.
  10. {"user_types": {}} — loads "successfully" and shows one lone User node. {"user_types": []} does the same, because the check is typeof user_types === 'object' and an array passes it.
  11. SQL type namesvarchar, int, uuid, timestamp, jsonb. All render as their literal string.
  12. date_range / numeric_range / date_interval — real Bubble types, meaningless here.
  13. list.geographic_address — not a list.
  14. Bubble's automatic fieldsid, created_at, modified_date, creator, slug. Bubble adds these itself; including them clutters the diagram and, if you later paste into Bubble, collides with the built-ins.
  15. A key reused in both user_types and option_sets — the two share one namespace on the diagram, so one silently overwrites the other.

Did it work

Check the diagram, not the JSON. Six things:

  1. No red boxes. Red means [Missing: …], which means a reference typo.
  2. Every box has a real name. A box reading undefined means a missing display.
  3. Every list-of-records field shows a link. If a field you meant as a list has no line running out of it, the type string degraded — check for list.user.
  4. The colours are right. Blue is a data type, orange an option set, gold the built-in User.
  5. The count matches. If you asked for twelve types and see nine, three collided on a duplicate key.
  6. Toggle Show deleted. If items appear, something was marked deleted: true.
Check 1, failing. Two red boxes from two reference typos: custom.products where the key is product, and option.orderStatus where the key is order_status. The file loaded without a single error.

If any check fails, paste the specific problem back to the AI — "the watchers field on Order has no link" — and re-upload. That loop is usually one round.

What the parser does not do

Worth stating plainly, because it explains why the checklist above is necessary:

  • No schema validation. There is no JSON Schema, no zod, no ajv.
  • No type checking beyond the grammar table.
  • No cross-reference checking. Dangling references become placeholder boxes.
  • No duplicate detection.
  • No required-field checking, apart from user_types existing.

There is exactly one check: is user_types an object? Everything else is best-effort rendering.

Getting the file into LumiDevKit

Save the AI's output as a .json file and upload it to the Database Schema card on the Database Visualizer.

  • Accepted extensions: .json, .bubble, .sql
  • Maximum size: 50 MB

You can also paste JSON rather than uploading a file — but note that the paste box on that screen sits under a panel headed Postgres / SQL. Despite the label it accepts Bubble JSON too; the format is detected from the content, not the heading.

If the upload fails outright, the error decoder lists every message and its cause.

From LumiDevKit into Bubble

Once the diagram looks right, you can push the whole schema into a real Bubble app — every data type, every field, every option set — using the LumiDevKit extension. See Push a schema into Bubble.

View this page as plain markdown