Quickstart

Columbus, a static demo, from a chat with Claude to the community on Free.

On this page

This page takes one real project from a brand-new account to a public room on the community, on the Free plan. The project is Columbus, an interactive prototype of a private-markets introduction service: a Next.js app with every house, deal and figure in it invented.

Most people start in a chat with Claude. You sign up with GitHub, add Roomi to Claude as a connector, and say "deploy it to roomi": Claude learns the rules from the connector, checks the demo, rehearses the deploy, and publishes only when you say yes. A small static demo goes out from the chat itself. Columbus is bigger than a chat can carry, so Claude hands its deploy to pitch, the CLI, on your machine, and this page follows it there: pitch init describes the demo, pitch validate checks it, and pitch deploy rehearses it and publishes it. Then you write its community card and ask for it to be listed. Once it passes the checks, anyone can open it, and you see how many did.

The output on this page is what pitch printed when Columbus was first published to production, on 30 September 2026; only the folder's path is changed, to /Users/you. The last steps, the community card and listing, are described from what the app does, because Columbus had not been listed when this was written.

A demo with a server is Your first container demo.

Before you start

You need:

  • A demo on mock data, in a folder of its own. Everything in a static build is sent to every viewer's browser, and a listed project is public, so nothing in it may be real: no customer data, no keys, no real person's details.
  • Claude, on claude.ai or in the Claude app, to add the connector to.
  • For the CLI, Node 24 and gitleaks (brew install gitleaks), on a Mac. Install the CLI has the full list. A static demo needs no Docker.
  • The demo's dependencies installed (pnpm install for Columbus). pitch deploy runs your build script; it does not install anything first.

Columbus renders nothing on request, so it can be exported as files. Before this page starts it was changed to do that: output: 'export', trailingSlash: true and images: { unoptimized: true } in next.config.ts, and the theme read in the browser instead of from cookies(), which an export cannot call. Static demos has each change and why.

Sign up with GitHub

Open app.getroomi.com and choose Continue with GitHub. There is no separate sign-up: the first time you sign in, your account is made, with a workspace of your own on the Free plan. (Continue with Google does the same, for invited testers for now.)

On Free, a workspace has five projects, static demos only, two deploys in any 24 hours, and no private viewer links: a project is either a draft only your workspace can open, or listed on the community at one public address. Plans and limits has every limit.

Add Roomi to Claude

  1. In Claude, on claude.ai or in the Claude app, open Settings, then Connectors, and choose Add custom connector.

  2. Name it Roomi, and paste the address:

    https://mcp.getroomi.com/mcp
  3. Choose Connect. Claude sends you to Roomi: sign in, check what it may do, and choose Allow.

Deploying from Claude has the details, including a Team or Enterprise plan, where an owner adds it once for everyone.

Say "deploy it to roomi"

In a chat:

deploy it to roomi

Claude does the rest, and asks you before anything goes live. For a static demo it can hold in the conversation, a page or a small app, it goes like this:

  1. deploy_guide. Claude reads the connector's guide before it prepares anything: the build contract, the manifest, and how to write the tour.
  2. The demo and its tour. It writes the demo's files and drafts the manifest: its apps, a tour (story) and any pages. It shows you the tour, and keeps it on your yes.
  3. check_bundle, with exactly what it will deploy. It sends nothing, and names every problem at once with its fix; Claude fixes them and checks again.
  4. deploy_static without commit, a rehearsal. Claude shows you what would go live, and asks whether to publish it.
  5. deploy_static with commit: true, on your yes only. The room is live.
  6. What your plan allows next. On Free, which has no viewer links, Claude offers to list the room on the community, with set_community_card and request_listing. On a paid plan, create_link makes one viewer's link, and get_link shows it again if it is lost.

A chat carries a static demo of at most 200 files and 3 MB, with no server. Deploying from Claude has every argument and every check.

When the demo is bigger than a chat

Columbus is a repository on your disk with a build to run, and its export is over 400 files and 9 MB: more than a chat can carry. The connector tells Claude what to do then. The CLI, on your own machine, is the better route for a build over the connector's limits, a demo with a server, or work in a repository with Claude Code, so Claude says so once and gives you two commands:

npm install -g @pitch-product/cli
pitch login

In Claude Code, which has a terminal, Claude offers to run the install for you, and runs it only on your yes. pitch login opens your browser for you to approve, so you run that one yourself. On claude.ai or in the Claude app, paste both into a terminal.

The rest of this page is the CLI's part, step by step: what Claude Code runs for you with the plugin, or what you run yourself. The chat stays useful once the CLI has deployed: links, activity, feedback and the community card all work from there, on the same project.

Install pitch

npm install -g @pitch-product/cli

That is the first of the two commands above, and it gives you the command pitch, which talks to Roomi with nothing to configure. To run it without installing, put npx @pitch-product/cli wherever this page says pitch: npx @pitch-product/cli login.

Sign in

pitch login

It prints an address and a code. Open the address, check the code matches the one in your terminal, and choose Yes, connect pitch. The token goes in the macOS Keychain. Install the CLI explains each step.

Describe the demo

In Columbus's folder:

pitch init

pitch init reads the repository and works out what it can: the name from package.json; that this is a static build, because it is a Next app with output: 'export'; and where the build writes it. Columbus's build script sets NEXT_DIST_DIR=.next-build, and next.config.ts reads that variable into distDir, which is where a Next export lands, so the output is .next-build. What it cannot tell, it asks. For Columbus that is one thing, the device, and Columbus is a desktop-first tool, so the answer is laptop:

  Read /Users/you/columbus-demo-app
    name "Columbus" from package.json
    output .next-build, the distDir in next.config.ts, with NEXT_DIST_DIR=.next-build from the build script
    kind static (next)
  static, with 1 app(s):
    app          /            ?       (not known)
  ? Which device is "Columbus" (/) for? (phone/tablet/laptop/screen/none) laptop
  Wrote pitch.json (1 app, static) and pitch.README.md, which explains it and shows a story's shape.
  Wrote PITCH.md, the guide for a coding agent: the rules, the build contract and the loop.
  Made AGENTS.md, one line pointing at PITCH.md.
  There is no story yet: https://getroomi.com/docs/story
  Next: `pitch validate`, then `pitch deploy` to rehearse. Nothing leaves this machine without --commit.

With nobody at the terminal to ask (a script, or a coding agent), pitch init writes nothing, and names the flag that answers each question instead: here --device app=laptop, so pitch init --device app=laptop does the same without asking.

pitch.json is the manifest, and the room is drawn from it. pitch.README.md holds the notes JSON cannot carry, and the shape of a tour on your own apps; nothing reads it, so delete it whenever you like. PITCH.md is a guide for the coding agent working in the repository, and AGENTS.md now points at it (if the repository already has an AGENTS.md or a CLAUDE.md, it adds one line to the end of each instead). See Build with your AI agent.

pitch init never edits your code, and never replaces any of its files unless you pass --force. pitch init --print shows what it would write without writing anything.

Add the tour

pitch init wrote one app, Columbus at /, and Columbus is one app however many of its pages the tour visits. A story is the guided tour the room offers each viewer: a few steps, each saying what to notice, which app to show, and which of its pages to open (path). The last step shows the first page again on a phone (device), then the laptop comes back. This is pitch.json with a five-step story, and the app's id changed from app to columbus, since every step names it:

{
  "$schema": "https://getroomi.com/schema/pitch.json",
  "name": "Columbus",
  "kind": "static",
  "output": ".next-build",
  "apps": [{ "id": "columbus", "name": "Columbus", "path": "/", "device": "laptop" }],
  "story": [
    {
      "id": "briefing",
      "title": "What needs the house today",
      "say": "Arundel House, an invented family office, opens Columbus on a concierge rather than a feed: two things are waiting on them, one closes in four days, and nothing else needs them today.",
      "app": "columbus",
      "path": "/"
    },
    {
      "id": "score",
      "title": "A score with its reasons",
      "say": "Project Kestrel scores 74 against the house's own mandate. Each of the eight axes gives its reason, and the binding one is named: a 10–12% target sits below the house's 12–18% floor.",
      "app": "columbus",
      "path": "/transactions/kestrel/overview/"
    },
    {
      "id": "table",
      "title": "A seat at the table",
      "say": "Four family offices take Project Anvil's £40m round between them. Arundel House's seat is £12.0m, and the cap table shows what it would hold: 7.4% fully diluted.",
      "app": "columbus",
      "path": "/transactions/anvil/structure/"
    },
    {
      "id": "audited",
      "title": "The score, held to account",
      "say": "Columbus keeps the score it gave on the day the house committed, and sets it beside what happened: Thornbury scored 91 at entry and has returned 2.00× since. Old scores are never revised.",
      "app": "columbus",
      "path": "/portfolio/thornbury/overview/"
    },
    {
      "id": "phone",
      "title": "The same briefing on a phone",
      "say": "On a phone the navigation folds into a drawer and tables become cards, so a principal can read what is waiting on them between meetings.",
      "app": "columbus",
      "path": "/",
      "device": "phone"
    }
  ]
}

The first line points at the published schema, so your editor completes and checks every field. Writing the tour explains why each step is written the way it is. You can deploy without a story and add it later.

Check it

pitch validate
  ok   manifest: pitch.json validates (static, 1 app, a 5-step story)
  Nothing was built or sent. `pitch deploy` rehearses the whole deploy.

pitch validate checks pitch.json against the contract without building anything. A problem stops a deploy, and each one names the docs entry that explains it; a note is advice and stops nothing. If the app has never been built, it notes that .next-build/index.html is not there yet, which is expected: pitch deploy runs the build first.

See the room

pnpm dev        # Columbus's own dev server, in one terminal
pitch dev       # the room around it, in another

pitch dev opens the room a viewer will get, on your machine, with Columbus's dev server on the stage: take the tour, open each step's page, try comment mode. Nothing in it is recorded or sent, and when you change the story in pitch.json the room redraws. What is wrong, a step's page that answers 404 say, is counted on the room and printed in the terminal. Preview locally has the rest.

Rehearse

pitch deploy

Without --commit, pitch deploy is a rehearsal. It runs every check pitch validate does, scans the project for secrets, runs your build script with the package manager your lockfile names, checks that every page the story opens is in what it built, scans that, and shows what would go live. The project scan leaves out the folders tools generate, .next and .next-build among them, where Next writes keys of its own; the scan of what it built reads every file that would be uploaded:

  ok   manifest: pitch.json validates (static, 1 app, a 5-step story)
  ok   secrets: gitleaks scanned the project and found nothing
  ok   build: pnpm run build
  ok   story: the 5 pages it opens are in the build
  ok   secrets: gitleaks scanned the build output (.next-build) and found nothing

  What would go live: project "columbus" at https://api.getroomi.com
    Columbus — static, from .next-build/
    artefact 1.4 MB (1460143 bytes)
    apps:
      columbus     /            laptop  Columbus
    story:
       1 briefing     in columbus at /  "What needs the house today"
       2 score        in columbus at /transactions/kestrel/overview/  "A score with its reasons"
       3 table        in columbus at /transactions/anvil/structure/  "A seat at the table"
       4 audited      in columbus at /portfolio/thornbury/overview/  "The score, held to account"
       5 phone        in columbus at / (phone)  "The same briefing on a phone"

  Rehearsal only: nothing has left this machine. `pitch deploy --commit` publishes exactly this.

Nothing has been sent anywhere, and a rehearsal does not count against the day's deploys. The project is called columbus because its address is made from the name in pitch.json: lower-case letters, digits and hyphens.

A static rehearsal builds and scans; it does not open the demo. Look at the built folder in a browser yourself first, the way a viewer will see it.

Publish

pitch deploy --commit

This runs the same checks and the same build again, then publishes exactly what it checked. After the summary of what would go live, it prints:

  ok   project: created "columbus"
  ok   upload: 1.4 MB
  ok   build: registered cc18ca35-62c1-421a-a80a-0d24058fe093, with the story's rehearsal (5 steps)
  ok   live: build cc18ca35-62c1-421a-a80a-0d24058fe093

  Live, as a draft: only your workspace can open it. Free has no viewer links.
  `pitch open` shows you the room as a viewer sees it.
  To show it to anyone, list it on the Community: `pitch community card` writes the card strangers see, and `pitch community list` lists it once Roomi's checks pass, each rehearsed until `--commit`.

The first deploy creates the project. Each later one adds a build and makes it live; earlier builds are kept, and the app's Builds tab can make any of them live again. This counts as one of Free's two deploys in 24 hours. The story's rehearsal goes with the build, so the project's Overview shows the tour step by step; on a static build every step has nothing to run.

The last three lines are what pitch deploy --commit says on Free of a project that is not listed yet: there are no viewer links, so the next steps are to look at it yourself and to list it. On a paid plan it says instead that the room opens only through a viewer's own link, and that pitch link <name> makes one.

Look at it as a viewer

pitch open

For a project that is not listed, pitch open asks the platform for a one-time pass and opens the room with it, exactly as a viewer would see it and marked as a preview. Preview as viewer, in the project's header in the app, does the same. A preview records nothing.

Take the tour from the room's card, and check that each step opens the page its words describe, and that the last one shows it on a phone.

Write the community card

The card is what strangers see on the community: a title, one line and a cover. It is separate from the pitch, so write it for someone who does not know the business.

The quickest way is the chat you started in. Ask Claude to "list Columbus on the community": it drafts the card from the tour and rehearses it with set_community_card, which runs the check below, saves it on your yes, then asks to list it with request_listing, again on your yes. community_card says where the listing stands. pitch community does the same from the terminal: Listing on the Community has both. Here it is in the app: open the project's Community tab (the line at the top of every project has a List on the Community button that goes straight there), under Community listing:

  • Title (up to 60 characters): Columbus: a private-markets introduction prototype
  • One line (up to 140): An interactive prototype: invented family offices, invented deals, and a suitability score with its reasons written out.

Choose Save the card. A cover (PNG, JPEG or WebP, up to 1 MB) is optional; add it once the words are saved.

Ask to list it

Choose List on the community. It needs a saved card and a live build, and it does two things there and then: it runs a quick check of the live build's files, your pages and the card for anything that might identify a real business, and it runs Roomi's checks on the card and the build. If they pass, Columbus is listed at once.

The quick check warns and never blocks. What it noticed is listed under What the check noticed, and Roomi may look at a listing it names. Columbus's export is larger than the check reads in full, so its list ends with what it did not read: one line naming how many text files it had no room for, and the first few worth reading yourself.

For a warning that names something real, change it in the demo and deploy again, or change the card, then ask again. The community has every kind of warning and what the check reads.

The checks

Roomi's checks look at the card's words, the project's name and address, and the build: a list of terms that are never listed, a program or an installer in the build, a password sent to another site, a cryptocurrency miner, and a name that borrows a brand. Passed, the card says On the Community. Failed, it says Blocked by a check and which part and why; change it and choose Ask to list it again, or Ask for a review if you think the check got it wrong.

Editing a listed card's words or cover, or deploying a new build, runs the checks again: it stays listed unless one fails. A new cover shows once its own check has passed. Roomi may also take a listing off later, with a comment saying why, which you read beside the card and in your notices.

The public address

Once listed, Columbus answers to anyone at its public address, https://columbus.getroomi.app: the project's address, columbus, as pitch deploy --commit created it. pitch open then opens that address and prints it, and the next pitch deploy --commit ends with it (Live: https://columbus.getroomi.app). Until it is listed, and again if it is unlisted, blocked or taken off, that address is the same Not found as one that never existed.

Visitors need no link and no account:

  • They are anonymous. The room counts them by an id it gives each browser session, stored hashed, and never says who they are.
  • Five at once. At most five people are in the room at a time. The sixth is told the demo is full and is counted as turned away.
  • One copy, shared. A static build's files are the same for everyone; what the app keeps in the browser is each visitor's own.
  • To comment, react or ask a question, they sign in with GitHub or Google. The room sends them to the app's sign-in page and straight back, and you see their name and picture, never their email. "Ask a question" is the only next step a public room offers.

What you see on Free

The project's Analytics shows the range's total views, and the Overview shows how many were turned away this month. The rest of the Analytics screen is drawn from invented values under a lock headed Upgrade to unlock, which says what a paid plan shows: never your numbers blurred, because the platform does not compute them for a Free workspace. Each viewer's journey and the pipeline are behind the same lock. Paid plans cannot be bought yet, so the lock says "Paid plans are coming soon" rather than offering a button. Analytics and the pipeline has the detail.

Next