Skip to main content
There are two places the truth about your backend can live, and the choice shapes everything else:
  • Your repo. TypeScript is authoritative; the workspace is a deployment target. Changes arrive through pull requests. This is flows 1–3.
  • Your Xano workspace. The workspace is authoritative; code is pulled out, worked on, and pushed back. Changes arrive through Xano branches, and git may not be involved at all. This is flow 4.
Pick one per workspace. Both directions work, but only one of them is a merge, so a workspace that is authoritative on Mondays and a deployment target on Tuesdays will lose someone’s work.

The three places XanoTS writes

Reading back out is xanots init <dir> --from workspace, which turns the live branch into TypeScript. See Pulling an existing workspace.

What goes in git

For the flows where the repo is authoritative:

Commit

xano/** (your defs), xano.lock, package.json, and your frontend source. The lock pins every object’s identity — without it a release can duplicate objects instead of updating them.

Never commit

.xano/auth.json (xanots login --local gitignores it for you), .env, node_modules, frontend/dist, and any exported bundle — a bundle carries your workspace’s environment variable values.
A pulled xano/workspace.ts carries environment variable values inline, because that is what a deploy sends. Read it before the first commit and move real secrets out.

1. Solo developer, repo is the truth

One person, main, one workspace. Branches in git are optional; the ephemeral is where you try things.
1

Sign in once

2

Work, and deploy as often as you like

The ephemeral is disposable and expires on its own, so a bad deploy costs you nothing.
3

Prove it, then merge

4

Release to production

Always --dry-run first. It prints exactly which objects would be created, updated, or dropped — including any table column your schema no longer defines, which takes its data with it.

2. A team, all on XanoTS, repo is the truth

Same shape, plus review. What makes it work is that everybody’s deploy goes to their own ephemeral, so two people can be mid-change at once without touching each other or production.
1

Everyone signs in to the same workspace

There is no --workspace flag — one credential addresses one workspace. xanots workspace details says which.
2

Branch, deploy, share a URL

Paste the printed URL into the PR. A reviewer gets a running backend and frontend to click through, not just a diff. xanots ephemeral list shows what everyone has up; xanots ephemeral delete <tenant> --yes cleans one up early.
3

Open the PR

Review the TypeScript diff like any other code. Two things a normal code review misses:
  • a removed table column or removed table — that is data loss on release;
  • a change to xano.lock that isn’t a rename you expected.
4

Merge to main, then release

One person (or the pipeline in flow 3) releases from main:
Renames need one extra step. Renaming an object in code looks like a delete plus a create unless you move its lock entry:
Commit the resulting xano.lock in the same PR as the rename. Merge conflicts in xano.lock are normal — two branches each added objects. Take both sides, then re-run xanots export ./xano/index.ts to rewrite the file cleanly and commit that.

3. Automated: main is what production runs

Same commands, run by a machine. Any runner works — the requirement is three environment variables, the credential shape that never expires or rotates:
Never run xanots login in a pipeline — it blocks on browser consent. And set all three variables together; setting some is a hard error, not a quiet fallback.
On every pull request — build it, prove it, leave a live URL behind:
--strict turns build warnings into failures. --frozen-lock fails if the export would change xano.lock, which catches someone who forgot to commit it. --test runs the deployed tests; a failing suite exits 5. On merge to main — reconcile production:
Safe to run on every merge, and safe to re-run. When the workspace already matches the code, nothing is sent at all — the summary reports "upToDate": true and no updated_at moves. --yes skips the confirmation prompt but never skips the printed preview, so the log still shows what changed. A staged pipeline puts the merge on a branch and makes promotion a separate, human button:
--prune (delete objects the project no longer defines) is deliberately absent above. Deletions are worth a human reading the plan: xanots release ./xano/index.ts --prune --dry-run.
Nightly, or before a release — check the code against a real engine without touching anything you own:

4. The workspace is the truth, and branches are the merge

This is the flow for a team that lives in Xano. Nobody keeps a long-lived repo. Everyone — whether they write TypeScript, read XanoScript, or build in the no-code builder — does the same four things:
  1. Take a copy of what is live.
  2. Work on it, trying it out on a disposable ephemeral.
  3. Push it back as a branch, which stages it without serving it.
  4. Promote the branch, which is the merge.

First, what a Xano branch actually isolates

Branches scope logic, not data. API groups, queries, functions, tasks, triggers, middleware, tools, toolsets, channels, realtime servers, knowledge, addons and messages each belong to a branch. Tables and microservices do not — one set is shared by every branch of the workspace.So a release that adds a column reaches production the moment it lands, even on a branch, and the plan describes it as routine. A branch stages your logic and shares your schema.
The CLI refuses rather than letting that happen quietly. release --branch reads the live workspace, diffs the shared objects, and stops with the tables named:
Your three answers: take the schema change out of this release; drop --branch because applying it is what you wanted; or pass --allow-shared-schema-changes to stage the logic and apply the schema knowing both happen. If the live workspace could not be read, that is also a refusal — a failed read is not evidence that there is no schema change.

The TypeScript developer, and the XanoScript developer

One flow, two artifacts. No repo — the working copy is a folder you delete when you are done.
1

Pull the live branch

The pull preserves each object’s guid, so what you push back matches in place rather than duplicating. xano/README.md lists anything that did not translate cleanly — read it before you start editing.
2

Work on it against an ephemeral

Nothing here can reach production. Deploy as many times as you like.Reading XanoScript rather than TypeScript? The deployed ephemeral exports as a multidoc:
3

Push it back as a branch

Nothing serves orders yet — the live branch is untouched. --backup-branch snapshots the live branch’s logic first, so the promote in the next step has something to go back to. The release refuses outright unless the instance confirms it planned against the branch you named, so an instance without branch support fails closed instead of overwriting production.
4

Review it in the builder

Open the branch in Xano and click through it. This is the review step — there is no diff to read, so someone has to look.
5

Promote — this is the merge

A production cutover: the runtime stops serving the old branch and starts serving this one. Table data is unaffected either way, because it was always shared. The CLI names the outgoing branch and prompts before doing it (--yes in a script).
6

Clean up

Keep the branch you just replaced until you are confident — it is your rollback. The live branch cannot be deleted; promote another one first.

The builder developer

Someone building in the Xano no-code UI does steps 1, 2 and 3 in the builder itself: they work on a branch in Xano rather than on the live one, and click through it there. Then the last step is identical, from the builder or the CLI:
They never pull, never install anything, and never touch an ephemeral. The whole team meets at the same place — a branch, promoted to live — which is why this arrangement works and the mixed git/UI one does not.

Rolling back

The branch you replaced is still there, so undoing a promote is one command:
That restores logic. It does not restore tables, because tables were never on a branch — a dropped column is gone from every branch at once. Read the shared-schema report before you release, not after.
v1 is the label every workspace starts with, and it is reserved — --branch v1 is refused rather than silently writing to whatever is live. Pick your own labels.

Other arrangements

Going the other way — a workspace that exists, becoming a repo that is authoritative. Pull it, commit it, and capture its identities before you ever release:
The engine assigned those objects random guids. Nothing in your code can re-derive them, so this xano.lock is irreplaceable — losing it means the next release duplicates every object instead of updating it. From here you are in flow 2, and the workspace stops being a place people edit.
Nothing extra to configure: a PR job already deploys to an ephemeral and prints the URL. Post it as a PR comment and let it expire on its own, or tear it down on merge:
In a repo-is-the-truth workspace this is a divergence: it exists live and not in your code, so the next release does not know about it and a --prune would remove it. Get it into git the same day:
Then PR it. If the diff shows more than the hotfix, someone else has been in there too.
The agent authors TypeScript and deploys to ephemerals, so it slots into flow 1 or 2 as a contributor: it works on a branch, you read the diff, you merge. Keep xanots release off the agent’s path — let it deploy freely and let a human release. See Bolt and Lovable.
Each entry file is its own workspace and gets its own credential, its own xano.lock beside it, and its own release. Point every command at the entry explicitly:
lock rename and friends look for xano.lock in the current directory unless you pass --entry=<path>, so always pass it in a multi-backend repo.

The rules that survive every flow

  1. Pick one source of truth per workspace and say so out loud. The repo, or the workspace.
  2. Ephemerals are free; production is not. Iterate on deploy, promote with release.
  3. --dry-run before a release, and read the column drops.
  4. A branch stages logic and shares schema. If the shared-schema check refuses, it is telling you something true.
  5. xano.lock is committed, always — wherever a repo is authoritative. It is what makes a release update instead of duplicate.
  6. Credentials never enter git. People use xanots login; machines use the three environment variables.