dabadataDocs
Start / Build with an AI agent

Start

Build with an AI agent

Send one link and the other person's AI agent knows how to build on dabadata, the realtime app backend: this page. The agent reads a short index written for it, learns the API, the rules and the safe way to write, then builds the app with your project's keys - and, through the hosted MCP server, can put that app inside ChatGPT or Claude, where people use it with live widgets.

To a person, send dabadata.com/docs/agents/. To their agent, the address that matters is https://dabadata.com/llms.txt: a plain-text index in the llms.txt format with the exact first steps and a link to every page as markdown. The rest of the kit:

AddressWhat it is
/llms.txtThe index: what dabadata is, "Start here" in seven steps, every feature group, every docs page with one line about it.
/llms-medium.txtEvery docs page in brief: its description and each section's first paragraph, small enough for a chat assistant's browser.
/llms-full.txtEvery docs page in one markdown file, in reading order, for agents that prefer to read everything at once.
/docs/<page>/index.mdEach docs page as clean markdown, for example /docs/concurrency/index.md.
/v1/openapi.jsonEvery HTTP route as OpenAPI 3.1: credentials, request and response shapes, error bodies.
/mcp/<project id>The project's own MCP server, once the definition turns it on.

Give the agent the keys

Sign up (free, no card), open the console's Keys page and switch to the test environment: build there first. Copy the publishable key, and press Rotate next to the secret key to see a new one (a secret key is shown once). Put both in the project's .env, which the agent can read and your browser code never ships:

bash
DABADATA_URL=https://dabadata.com
DABADATA_PUBLISHABLE_KEY=pk_test_...   # safe in a browser: names the project, grants nothing
DABADATA_SECRET_KEY=sk_test_...        # server only: bypasses every rule

The console's Apps page has the same keys, the project's MCP address and a ready prompt built from them.

The prompt

Paste this into Claude Code, Cursor, Codex or any agent that can read the web and your files, and replace the last line with what you want:

text
Read https://dabadata.com/llms.txt and follow it. My dabadata project keys are in .env
as DABADATA_URL, DABADATA_PUBLISHABLE_KEY and DABADATA_SECRET_KEY (the test
environment). Define the tables and rules with PUT /v1/schema - a dry run first, then
apply. Use atomic operators or /v1/transact for anything that changes a count or a
balance, keep the secret key on the server, and tell me how to open the result.

Build: a shared expenses app for my flat - who paid what, and who owes whom.

The agent comes back with a definition (tables, columns, one rule per operation), applies it, and writes the app around the JavaScript client. When a call is refused, the error carries the rule's explanation and a trace id, so the agent can fix the rule or the call instead of guessing.

Connect the MCP server

Every environment can be an MCP server. Turn it on in the definition (the agent can do this too):

json
"mcp": { "enabled": true, "name": "Team expenses" }

The address is https://dabadata.com/mcp/<project id> for live and https://dabadata.com/mcp/<project id>_test for test. What a client may do depends on the credential it brings: the secret key gets the developer tools (build and debug the app, rules bypassed); an end user who signed in gets the app's own tools, under the rules.

Claude Code

bash
claude mcp add --transport http dabadata https://dabadata.com/mcp/prj_example12345_test \
  --header "Authorization: Bearer $DABADATA_SECRET_KEY"

Cursor

In .cursor/mcp.json (keep the file out of git, it holds the key):

json
{
  "mcpServers": {
    "dabadata": {
      "url": "https://dabadata.com/mcp/prj_example12345_test",
      "headers": { "Authorization": "Bearer sk_test_..." }
    }
  }
}

ChatGPT, claude.ai and Claude Desktop

This is how people use the app inside the chat, with tables, forms and records drawn as interactive widgets that update live. Add the MCP address as a custom connector (in ChatGPT under the apps and connectors settings, with developer mode on; in Claude under Settings, Connectors, "Add custom connector"). There is no key to paste: when the project has hosted end-user accounts turned on (auth.users.enabled), the chat starts an OAuth sign-in, the person signs in or signs up on the project's own page, and every call the chat makes is that person's, held to the rules and written to the trace with source: "mcp".

What the agent can do

With the secret key, the agent gets the developer tools besides the app's own:

ToolWhat it does
describe_appTables, columns, tools, and for the caller whether each of read, create, update and delete is allowed, with the rule's explanation. The first call.
get_definitionThe definition in force, as JSON.
apply_definitionValidate and apply a definition, exactly like PUT /v1/schema. With dryRun: true it answers the plan and changes nothing; every problem comes back at once.
widget_put, widgets_listStore a custom widget (one HTML document, served as ui://app/<name>) and point a table at it with mcp.tables.<table>.widget.
transactSeveral writes and checks in one all-or-nothing transaction, like POST /v1/transact.
trace_recentThe newest trace rows: who did what, the rule, the decision and why.
simulateWould this user be allowed this operation? Nothing changes, nothing is traced.
sql_readRead-only SQL on the environment's tables.
<table>_list, _get, _create, _update, _deleteThe app's own tools, one set per table. These are what people in a chat get, under the rules.

The safe-writes rule

Never read a value, compute a new one and write it back: two people doing it at once both write the same number and one update is lost. In the measurement, with 256 shoppers at once fewer than 2% of naive purchases came out right. Ask the database to do the arithmetic instead:

ts
// Wrong: read, compute, write
const p = await db.get("products", id);
await db.update("products", id, { stock: p.stock - 1 });

// Right: an operator, computed inside the UPDATE
await db.update("products", id, { stock: db.dec(1) });

// Right: several rows, all or nothing, with a check that must hold
await db.transact([
  { op: "check", table: "products", id, expect: { stock: { $gte: 1 } } },
  { op: "update", table: "products", id, values: { stock: { $dec: 1 } } },
  { op: "insert", table: "orders", values: { product: id, qty: 1 } },
]);

Add unique columns and checks such as "stock_nonneg": "stock >= 0" to the definition so the database refuses what must never happen. Safe concurrent writes has every operator, expect, ifAbsent and the transaction rules.

Start from a template

A template is a ready definition (tables, rules and the mcp block) plus its custom widgets. The curated ones are public:

bash
curl https://dabadata.com/v1/templates
json
{ "templates": [ { "slug": "expenses", "title": "Shared expenses", "description": "...", "tables": ["expenses"] } ] }

Pick one when you create a project in the console (or the agent sends "template": "<slug>" with POST /v1/workspaces/:id/projects), then ask the agent to change it rather than start from nothing. The console's Apps page lists them too.

Example: shared expenses inside ChatGPT in five steps

  1. Connect your coding agent. Sign up, copy the test keys into .env and add the MCP server to Claude Code as above (the definition needs "mcp": { "enabled": true } first: put it in on the console's Definition page, or let the agent do it over HTTP).
  2. Describe the app. "Read https://dabadata.com/llms.txt. Make a shared expenses app: anyone signed in sees the expenses, adds their own, and edits or deletes only what they paid. Amounts are positive. People sign up by themselves. Show the expenses as a table in the chat."
  3. The agent applies it. It calls describe_app, then apply_definition with dryRun: true, reads the plan, and applies:
    json
    {
      "roles": ["admin", "member"],
      "tables": {
        "expenses": {
          "columns": {
            "title": { "type": "text" },
            "amount": { "type": "float" },
            "paid_by": { "type": "userId" },
            "settled": { "type": "bool", "default": false }
          },
          "checks": { "positive": "amount > 0" },
          "rules": {
            "read": "authenticated",
            "create": "authenticated",
            "update": { "any": [{ "role": "admin" }, { "owner": "paid_by" }] },
            "delete": { "any": [{ "role": "admin" }, { "owner": "paid_by" }] }
          }
        }
      },
      "auth": { "users": { "enabled": true, "defaultRole": "member" } },
      "mcp": {
        "enabled": true,
        "name": "Team expenses",
        "instructions": "Track shared expenses. Amounts are in euros.",
        "tables": { "expenses": { "widget": "table", "describe": "One row per expense" } }
      }
    }
    It checks the result with simulate ("may a member delete someone else's expense?" - denied, with the reason) and, if you want a summary card, writes a custom widget with widget_put.
  4. Add the connector. In ChatGPT add a custom connector with https://dabadata.com/mcp/prj_example12345_test. ChatGPT opens the project's sign-in page; you sign up with your email and are back in the chat. Your flatmates do the same.
  5. Use it. "I paid 42.50 for dinner." The chat calls expenses_create as you and shows the expenses table. When a flatmate adds a row from their own chat, it appears in yours while you watch: the widget holds a live, read-only subscription under your read rule. Every call is in the console's Trace, with who made it and the rule that let it through.

When it works on test, apply the same definition to live (the console's Definition page, or the agent with the live secret key) and add the live address as the connector.

dabadata documentation, written against API contract v2.