# Ripping docs — full text Generated from https://docs.getripping.com. Sections: Guide, Building apps, Payments, For agents. --- # Getting started https://docs.getripping.com/guide/getting-started From the idea box on getripping.com to a running app, including the free first build and what it contains. ## Describe the idea The idea box is on the home page and on **New project** once you are signed in. It takes up to 1,000 characters; a ring next to the button shows how full it is. Four characters is the minimum. 1. Pick what you are making from the tabs on the box: **Web app**, **Mobile app**, **AI Agent**, **Website** or **Extension**. Mobile builds phone-first, Extension builds a Chrome extension; the rest build for the web. 2. Type the idea, or press the microphone and say it. Dictation adds to what is already in the box. 3. Optional: drop a reference image on the box (PNG, JPEG or WebP). The builder sees it once. 4. Optional: **Improve my prompt** turns a rough note into a structured brief with bullets. **Undo** puts your words back. 5. The model picker shows which Ripping is building: **Ripping 3.3**, **Ripping 3.3 Zip**, **Ripping 4.1** (the default) or **Ripping 5.0**. **Ripping 6.0 Max** is listed as "soon" and cannot be picked yet. 6. Press Enter, or **Build it**. Signed out, the button says **Get Ripping**. Under the box, **Try:** offers three example briefs; the refresh button draws three more. ## Sign up Signed out, the brief is kept and you go to the account screen. - Email and password (at least 8 characters). A 6-digit code arrives by email; the build starts the moment you enter it. - **Continue with Google**: the address is already verified, so the build starts at once. The brief is stored against your account before anything else happens, so a closed tab does not lose it. ## The first build The project opens on a full-screen checklist headed **First build**. **Starting point.** A line reads "Building this as *App*" (or *Course platform*, *Internal tool*, *SaaS*, *Website*, *Chrome extension*). It was picked from your brief. Press **Change** to pick another before anything is built; the project's files are reset to that starter. Once the first build has started this cannot change. **Step 1: What do you need?** Tick any of **Landing Page**, **Sign Up + Sign In**, **App Dashboard**. Ticking nothing is a real answer: the build leaves those out and deletes them from the starter. **Up to three questions** read from your brief, each with up to four answers and a **Something else** box. **Back**, **Skip** and **Next**; the last button is **Build it**. Keys 1 to 9 pick an answer, Enter goes on. **Skip the questions and build now** starts on the defaults. Then the checklist fills in: Prompt received, Create the project, Set up sign-in, a landing page and your design, Open a live preview, Warm up the builder, then the builder's own steps as it does them. A first build usually takes a minute or two. When it finishes the screen says **It's alive.** and the editor appears. If it stops, the screen says "The first build stopped on *name*" with the reason, and offers **Try the first build again** or **Open the editor**. The project is saved either way. ## What it costs Nothing. The first build does not come out of your tokens. It has its own room of 400 tokens, and if the build runs out of time and continues itself, up to four chained turns (each within ten minutes of the last) are still free. See [Plans and tokens](/guide/plans-and-tokens). One free build per account. If the account already has a project, the home-page brief is refused with "You already have a project". ## What the first build contains The **App** starter is a working app before the builder touches it: - A landing page in front, with sign in and create account. - Accounts: email and password, **Continue with Google**, forgot password, a welcome screen. - The app shell: a home screen with a menu, a settings screen, an account menu, a **Purchases** page (shown when the app sells something). - Privacy and Terms pages, a not-found page, an error screen. - Data that syncs across devices: private per-account storage, shared collections, file uploads. The builder customises this to your brief rather than rebuilding it. Two starters differ: **Internal tool** has no landing page and sign-up is invite-only from the start; **Website** has no accounts at all, and its contact form and newsletter land in your inbox (see [App users and data](/guide/app-users-and-data)). Every change the builder makes is type-checked and bundle-checked before it is handed back. If something breaks, it fixes that first. --- # The editor https://docs.getripping.com/guide/the-editor What each part of the build screen does, from the chat and its modes to the tabs on the right. ## Layout Chat on the left, your app on the right. On a phone the bottom bar switches between **Chat** and **Preview**. The header has **Projects** (back), the project name and status, the connection chips, **Add sign-in** (only on an older project without accounts), **Share**, and **Deploy** (or, for an extension, **Download extension** and **How to install**). ## Chat and turns Three modes sit above the conversation: - **Build**: "Changes the app. Every turn is a version." - **Chat**: "Questions and advice. Reads the code, never changes it." Answers can include a prompt you send to Build with one click. - **Design**: "The look. Instant, free, every change is a version." Opens the design panel (below). Type in the box and press Enter to send; Shift+Enter for a new line; start a line with `-` for bullets. The box takes 1,000 characters. Under it, "≈ *N* tokens · *reason*" updates as you type, so you know the price before you send. **Quality** (Build only): **Fast** builds straight away with no plan step and is cheapest; **Careful** (the default) plans first, then builds; **Best** plans, builds, then reviews its own changes and fixes what it finds. **Max Quality** tries the request several ways at once and keeps the best; it costs roughly three to four times a normal turn, so a dialog shows the estimate and asks before it runs. ## Phases Each turn shows a card with its steps: Prompt received, reading the brief, planning, building the screens, laying them out, saving data, polishing, writing up, testing. Steps tick as the builder does them, with the files written under each. When the turn finishes the card folds to one line, for example "1:42 · 6 files · 12.3k chars · ~140 tokens". The gear on the card sets the style (**Conversational**, **Terminal**, **Minimal**) and what stays visible. Under each reply: the tokens it cost, how many files changed, **Undo this turn** (files go back to before that turn), and thumbs up or down. After a build turn the preview reloads and is checked. The status on the right pane reads "visual check", then "visual check passed", "visual check found issues, fixing" or "preview error, fixing". Up to two fix turns run on their own; after that it leaves it to you. **Next** suggestions then appear above the box as one-tap follow-ups. ## Preview The **Preview** tab runs the app in a sandbox. Buttons on the tab bar: - **Select**: click an element in the preview, then say what should change about it. The builder gets the exact element and file line. - **Edit text**: click any words in the preview, type, press Enter. If those words appear once in the source they are changed there directly, no tokens spent, and a version is saved. Otherwise a toast says "Asking the builder." and a build turn makes the change. - Phone width, **Download the code** (a zip), and reload. An extension previews as a 380px popup hanging off a toolbar. ## Map **Map** draws the app's structure from its files, with no model call: pages by area, the menu, whether people sign in, whether a landing page is in front. Click a page to open it in the preview (signed-in pages open as a preview account). Click the file name to open it in **Code**. ## Code and History **Code** lists every file; each has its own history and can be put back on its own. `NOTES.md` is the builder's notes about the app, and you can edit it. **History** has one entry per build turn, newest marked **Current**. **Restore** brings a version back into the editor. **Put live** makes it the live app; **Take offline** removes the live app while keeping versions, data and users. The live version carries a **Live** badge. ## Design Switch the chat to **Design**. The panel is instant and costs no tokens: preset themes and **Shuffle**; six landing page layouts (**Centered**, **Split**, **Showcase**, **Story**, **Minimal**, **Feature led**) when the app has a landing page; colours, heading and body fonts, corners (0 to 32px), spacing, type size, shadow, and light, dark or auto mode. Describing a look in the box ("warmer", "bigger corners and a mint accent") is an AI edit. **Make the app follow it (AI edit)** rewrites hardcoded styles to use the tokens. ## The other tabs - **Data**: what the live app has stored, and the preview's own data. See [App users and data](/guide/app-users-and-data). - **Users**: who has an account, who may sign up, invites, blocking, admins. - **Errors**: crashes from the preview and the published app, each with a **Fix** button. The count shows on the tab. - **Sales**: every payment the app took. See [Products and sales](/payments/products-and-sales). - **Settings**: project name; tab title, meta description, favicon and search indexing for the published app; payments processor and products; custom domains; **Delete project**. **Save** sits at the bottom. --- # Templates https://docs.getripping.com/guide/templates What a template is, where the gallery appears, and what a click does in each place. ## What a template is A template is a finished, pre-built app with a brief attached. There are twelve: Habit tracker, Landing page, Personal CRM, Kanban board, Invoice tracker, Recipe box, Monthly budget, Workout log, Job applications, Reading list, Event page + RSVP, Metrics dashboard. Each tile shows a drawing of its first screen, a line about it and a tag (personal, work, finance, marketing). Some are built phone-first, some for the web. Landing page and Event page + RSVP start from the Website starter; Metrics dashboard from the Internal tool starter. ## On the home page The **Templates** section on getripping.com is for people who are not signed in. Tap a tile and: 1. The template's brief goes into the idea box, and the box scrolls into view. 2. The box remembers which template it came from. 3. When you press **Get Ripping** and create your account, the first build starts from that template's own files rather than from scratch, then applies your brief to them. Tapping a **Try:** idea clears the template link. ## On Your apps Signed in, the **Your apps** page keeps templates behind one line, **Start from a template**. Open it and tap a tile: - The template's files are copied into a new project of your own at once. The tile reads **Making it yours…**. No build runs and no tokens are spent. - The editor opens with the message "This is your own copy of the *name* template, ready to run. Tell me what to change." - History shows one version, "Started from the *name* template". If a template has not been built yet, the New project screen opens with its brief in the box, and a toast says why: "This template isn't built yet. Its brief is in the box instead." ## On New project The New project screen has the same tiles under **Or start from a template**, and behaves the same way: a built template becomes a project and opens; an unbuilt one seeds the box. When a brief is seeded, **Clear "*name*"** empties it. Under the tiles, **Already have something?** imports a zip (up to 25MB) or a repository from a connected GitHub account. The starter is laid down, the imported files sit beside it, and the first turn ports one into the other. ## Limits and errors - A project from a template counts against your plan's app limit like any other. On Free that is one app; the message is "Free covers one app. Move up to Singularity on the Billing page to build more." - A template's own project is a copy: changing yours never changes the template. - "No such template." or "This template has no files yet." mean the tile could not be turned into a project; the brief route still works. --- # App users and data https://docs.getripping.com/guide/app-users-and-data The accounts your app's users get, how sign-up is controlled, and what the Data tab shows. ## Accounts in your app Apps from the App starter have accounts from the first build: email and password, **Continue with Google**, forgot password. An older project without accounts gets them from the **Add sign-in** button in the header. Google sign-in uses Ripping's Google client by default, so Google's consent screen says Ripping. On the **Users** tab, **Use my own Google client** lets you paste a client ID and secret from Google Cloud (add the redirect URI shown there first); Google's screen then names your app. **Use Ripping's instead** switches back. ## The Users tab Counts at the top: total users, new today, new this week, active this week (signed in in the last 7 days), Google sign-ins. A chart shows users over time by day, week or month. Sign-ups from the preview count too. The table lists name, email, status, how they sign in, joined and last sign-in, with a search box. Per user: - **Block**: they are signed out everywhere and cannot sign in until you **Unblock**. - **Make admin**: an admin sees and changes everyone's shared records. **Remove admin** undoes it. - Remove (the bin icon): deletes the account and everything they saved in the app. ### Who can sign up - **Anyone**: anyone with the link can create an account. The default. - **Approve new users**: people can sign up, then wait. They appear under **Waiting for approval** with **Approve** (they get an email, "You're in: *app*") or **Decline** (the account is removed). - **Invite only**: only invited addresses can create an account. Others see "This app is invite-only. Ask its owner for an invite." Internal tools start in this mode. ### Invites Enter emails separated by commas (up to 50) and press **Invite**. An invited address can sign up even when sign-ups are closed, and someone already waiting for approval is let in. If the app is deployed, each person gets an email with the app's link; if not, the message says "Deploy the app to email them a link". Pending invites can be removed. ### Limits Your plan sets users per app (100 on Free, unlimited on Singularity). When full, a visitor sees "This app isn't taking new accounts right now." ## The Data tab Two sources at the top: - **Live**: what people saved in the deployed app, and in the preview when signed in. - **Preview**: the preview's own local data. You can set, remove or clear keys; a turn that changes the sample data resets it. **Live** lists shared collections with their rule, private data per person, and **Files** people uploaded, with storage used against your plan's limit. Pick a collection to see its records; edit one as JSON, delete it, or download the collection as **CSV**. Changes show in the live app right away. **Structure** folds out at the bottom: fields and types, the SQL, and the database connection. **Add a database** connects your own Supabase project: Ripping proposes tables, you run the SQL, and the builder rewires the app. ## Shared collections in plain words An app declares which data is shared and how in `src/lib/collections.ts`. Four rules: - **public**: anyone can read. Signed-in people add and edit their own. - **members**: signed-in people read everything and edit their own. - **inbox**: visitors add without signing in; people see only their own. The app's admins see everything. - **team**: shared within a team. Only the team's members read or write, and each record names its team. Teams and their members are collections too; membership is by email, so it applies the moment that address signs in. Data the app keeps per account (a person's own todos, settings) is private: only they see it in the app, and it appears under **Private, per person** on the Data tab. ## The website inbox A Website has no accounts. Its contact form writes to a `messages` inbox and its newsletter form to `subscribers`; the builder adds one inbox per extra form (a booking request, a quote). Each submission does two things: 1. It appears on the **Data** tab, where you read, export or delete it. 2. An email, "New message on *site name*", goes to every owner of your account with the fields as sent. Replying goes to the address the visitor typed, when they gave one. The site itself never shows submissions to visitors. --- # Connections https://docs.getripping.com/guide/connections The services your apps can use, in five pages, and how Slack notifications work. ## Five pages **Connections** in the sidebar has a page per kind of service: | Page | What's there | |---|---| | Marketing | Ad accounts (Google Ads, Meta Ads), with which app each one pays for; HubSpot for contacts and deals; Mailchimp for audiences. | | App Functions | Services your apps call from their own code: Slack, Notion, Google (Gmail and Calendar), Resend email, OpenAI, Supabase, any REST API. | | Account Functions | Services that work on your Ripping account: GitHub export, your own Anthropic key. | | Finance | Your own Stripe or PayPal, for apps that sell. | | Analytics | Google Analytics: a GA4 property and tag per published app, with the last week's figures. | A connection is made once per account and every app you build can use it. Secrets stay on the server; apps call through a proxy. ## Slack **Connect** on App Functions installs the Ripping bot in your Slack workspace with permission to post and to list channels. Two things follow: - **Your apps can post.** The builder knows `notify()` and `useChannels()` from `src/lib/slack.ts`; ask for "post to Slack when an order is placed" and it wires it. Only signed-in users of the app can trigger a post. - **Ripping posts your own events.** On an app's Settings → Notifications, pick a channel and tick what you want to hear about: a visitor's inbox message, a sign-up, a sale, a refund, a chargeback. Ripping posts these itself; the app needs no code. To post into a private channel, invite the bot to it in Slack first. ## Notion **Connect** on App Functions authorises the Ripping integration in your Notion workspace; while connecting, Notion asks which pages and databases to share with it, and you can share more later from a database's … menu → Connections. Two things follow: - **Your apps can read and write those databases.** The builder knows `useDatabases()`, `queryDatabase()` and `addRow()` from `src/lib/notion.ts`; ask for "save every booking to my Notion Bookings database" and it wires it. - **Inbox messages can be logged automatically.** On an app's Settings → Notifications, pick a database under "Log inbox messages to". Each message becomes a row; properties fill in where their names match the form's fields. ## Google Analytics **Connect** on the Analytics page signs you into Google with the Analytics permissions (a separate connection from Gmail and Calendar). Then every app you have published appears with a **Turn on** button: 1. Ripping creates a GA4 property named after the app in your Google Analytics account (you pick the account when you have more than one) and a web stream for the app's address. 2. The measurement id is kept on the app, and the GA tag goes into it at its next deploy. 3. The page shows users, sessions, page views and the top pages for the last seven days, per app. You need a Google Analytics account first; make one at analytics.google.com if the page says there is none. The app's own code never changes. ## HubSpot **Connect** on Marketing authorises Ripping in your HubSpot portal for contacts and deals. Then: - **Your apps can write to the CRM.** The builder knows `upsertContact()`, `createDeal()` and `useRecentContacts()` from `src/lib/hubspot.ts`; ask for "every sign-up becomes a HubSpot contact" or "each order becomes a deal" and it wires it. - **Inbox messages can become contacts automatically.** On an app's Settings → Notifications, switch on "Add inbox messages as HubSpot contacts". The message's email, name, phone and company fields are matched by name and the contact is created or updated, with lifecycle stage set to lead. ## Mailchimp **Connect** on Marketing authorises Ripping in your Mailchimp account. Then: - **Your apps can subscribe people.** The builder knows `subscribe()` and `useAudiences()` from `src/lib/mailchimp.ts`; ask for "a newsletter box that adds them to my audience" and it wires it. - **Inbox emails can join an audience automatically.** On an app's Settings → Notifications, pick the audience under "Add inbox emails to". Every message with an email is subscribed and tagged with the app's name. That audience is also where the app's own `subscribe()` goes when it names none. --- # Publishing https://docs.getripping.com/guide/publishing Deploying to a URL, connecting a custom domain, packaging a Chrome extension, and sharing a project. ## Deploy **Deploy** in the header ships the current files and gives the app its own address on Ripping's hosting (a `.vercel.app` address). After the first deploy the button reads **Update**. It goes grey when the live app already has these exact files ("The live app already has these changes") and comes back when anything changes. Deploying records a version; **History** shows which version is live. **Put live** on an older version is instant when that version was deployed before. Building never changes the live site; only Deploy and Put live do. What can stop it: - "There's nothing to deploy yet. Build the app first." - Plan limits. Free includes one live app; Singularity ten. Over the limit: "Your *plan* plan includes *N* live apps. Take another app offline from its History tab, or upgrade, to deploy this one." - "That's a lot of deploys in an hour. Try again in a few minutes." (30 deploys per hour per account.) **Take offline** (History tab) removes the live app. Versions, data and users stay; Deploy puts it back. ### How it appears On the **Settings** tab: tab title (empty uses what the app says about itself), meta description (300 characters; search engines show about 155), favicon (an emoji or a square PNG, JPEG or WebP), and **Let search engines index it** (off adds a noindex tag, useful while it is a draft). These apply the next time you deploy. ## Custom domains Domains need a paid plan (Free includes none; Singularity ten). Add one from the project's **Settings** tab under **Domain**, or from account Settings, where you can also buy one. 1. Deploy the app first. A domain connects on the app's first deploy; before that the status says "Deploy the app first. The domain connects on its first deploy." 2. Type the domain (`app.yourcompany.com` or `yourcompany.com`) and press **Add**. 3. The card shows the records to add at the company you bought the domain from: - A subdomain: `CNAME` with name `app` (the part before your domain) and value `cname.vercel-dns.com`. - A root domain: `A` with name `@` and value `76.76.21.21`. - Sometimes a `TXT` record to prove the domain is yours, shown only when needed. The card uses whatever values hosting recommends; copy what it shows rather than these defaults. 4. Wait for DNS (usually minutes, up to an hour) and press the refresh icon to check now. Status moves from **Waiting on DNS** to **Connected**. SSL is automatic. Errors you may see: "That doesn't look like a domain", "That domain is already added", "This domain is connected to another Vercel project. Remove it there first, then check again." Taking the app offline sets its domains back to pending until the next deploy. Invited users and approval emails link to a connected domain first, then the published address. ## Chrome extensions An extension project has no hosted URL. Instead of Deploy, the header has: - **Download extension**: a zip named `-extension.zip`. Inside: `manifest.json` (Manifest V3, version filled in, icons wired), `popup.html`, `popup.js`, `popup.css` (Tailwind compiled in), `background.js` and `content.js` when the project has them, `icons/icon16.png`, `icon48.png`, `icon128.png` drawn in your primary colour, and a `README.md` with the install steps. Nothing loads from the network at runtime. - **How to install**: 1. Click **Download extension** and unzip the file. 2. Open `chrome://extensions` and turn on **Developer mode** at the top right. 3. Click **Load unpacked** and choose the unzipped folder. 4. Pin it from the puzzle-piece menu next to the address bar, then click its icon. After a change, download again and click the reload arrow on the extension's card. The same zip uploads to the Chrome Web Store developer console. Downloads are limited to ten a minute; the button waits until the first build is done. ## Sharing **Share** in the header puts the project on a public page. Add one line about it (200 characters) and press **Make public**. The link is `getripping.com/p/`; copy it from the dialog. The page shows the running app, view and remix counts, a **Live site** link when deployed, and **Remix**, which copies the code into the visitor's own account (after sign-in). Your chat history and connections stay private. **Make private** takes it down. ## Exporting the code The download icon on the preview tab bar saves the whole project as a zip. Every file is also readable on the **Code** tab. --- # Plans and tokens https://docs.getripping.com/guide/plans-and-tokens What each plan includes, how tokens are counted and capped, and what the Billing page shows. ## Tokens A token is Ripping's unit of build work. 100 tokens is about a dollar of value. Measured across real builds, an average build turn costs about 135 tokens; the Billing page puts it as "100 tokens is about one small feature; a fresh app is around 300." A one-word tweak costs a fraction; a long prompt against a large project costs several times that. Every prompt shows its expected tokens under the composer before you send it, and each reply shows what it actually cost. Chat and Design turns are priced too, but the design panel's own controls (themes, colours, fonts, layouts) cost nothing. **Max Quality** runs several attempts and keeps the best; it costs roughly three to four times a normal turn and asks first. ## The first build is free The first build on an account does not touch its tokens. It has its own room of 400 tokens, and if it runs out of time and continues itself, up to four chained turns count as the same free build. The next day's edit is a new turn and pays for itself. ## Plans As shipped (the Billing page shows the current figures): | | Free | Singularity | Enterprise | |---|---|---|---| | Price | $0 | $45 a month per unit | Contact us | | Tokens a month | 50 | 5,000 per unit | 100,000+ | | Apps | 1 | 10 | 20 | | Live apps | 1 | 10 | 20 | | Custom domains | 0 | 10 | 50 | | File storage | 100 MB | 10 GB | 100 GB | | Users per app | 100 | Unlimited | Unlimited | | Agents | None | Up to 10 | Unlimited | Singularity scales by units: pick a **Size** from 1 to 20 units on the tier card or the slider, and the tokens and price scale together (2 units is $90 for 10,000 tokens). Changing size mid-month is prorated on the next invoice. The monthly allowance resets on your billing date and does not roll over. Tokens you buy on top (top-ups, from $10 for 1,000 tokens as shipped) are kept as a separate balance and spent after the allowance. ### App limit Creating a project past your plan's app count opens a dialog, "You have used your *plan* builds", with **Back to my apps** and **See plans**. Deleting an app frees the slot. ## The daily limit A quarter of the month's allowance is the most one day can spend, and never less than 50 tokens. It exists so one runaway session cannot use the whole month. When it trips, the chat shows "Daily brake: *N* tokens today, a quarter of your month, so one session can't spend it all. It lifts at midnight UTC; a top-up bypasses it." Purchased tokens are not braked. Other refusals: - At 80% of the month a warning bar appears above the chat. - "You've used this month's *N* tokens on the *plan* plan. Top up to keep going, or wait for the reset." - "You're out of tokens." when there is no allowance and no balance. - "Builds are paused right now while we look at something." is on Ripping's side; your tokens are untouched. Connecting your own Anthropic key on the Connections page makes turns that use it cost no tokens. ## When a plan ends Live apps stay online for 14 days at the old plan's limits, then pause until you renew. Data, files and domains are kept for 90 days. Renewing brings apps back as they were. ## The Billing page **Billing** (linked from Settings) shows: - **This month**: a bar of tokens used against the allowance plus balance, and the reset date. - The three tiers, with your current one marked, and the Singularity size selector. - **Receipts**: every invoice with a link to view it, and **Cards and billing details in Stripe** for the card on file. Only the account owner can change the plan. Payment goes through Stripe; after checkout the page says "Payment received. Your plan updates in a few seconds." --- # Affiliate program https://docs.getripping.com/guide/affiliates Earn a share of every payment from the accounts you send to Ripping, and how it is paid. ## What you earn Apply on **Affiliate** in your account (or from getripping.com/affiliates). Once approved you get a link like `https://www.getripping.com/?ref=YOURCODE`. Anyone who clicks it and creates an account within 60 days is your referral; the last link they clicked wins. You earn a share of every payment that account makes for a set number of months from its first payment, and they get a discount on their first month. The current rate, window, hold and minimum payout are published at getripping.com/affiliates; they are set on Admin → Affiliates, and an earning keeps the rate it was made at. ## When you are paid Each earning becomes payable after a hold that covers refunds. On the 1st of every month, if your payable balance is at least the minimum, it is transferred to your Ripping Payments account, which pays out to your bank on Stripe's schedule. If you haven't set up Ripping Payments yet (Money page), earnings wait for you. ## What the page shows Referred and paying counts, what is payable now, what is still in its 30-day wait, what has been paid out, every earning with its date, and every payout. ## Rules - No referring yourself, your own account or your own email address. - No paid search on the Ripping brand, no spam, no misleading claims. Affiliates who do that are paused and lose what has not yet been paid. - A refunded payment takes its commission back if it hasn't been paid out. - Rates can change with 30 days' notice; what you have earned stays earned. --- # Generated app anatomy https://docs.getripping.com/build/overview What a Ripping app is made of, where it runs, where its data lives, and which scaffold it started from. ## What a generated app is Every app Ripping builds is a Vite + React 19 + TypeScript project with Tailwind. It has no server of its own. Anything that needs one (accounts, saved data, uploads, payments, third-party APIs) goes through HTTP routes on Ripping under `/api/apps//`, and the app calls them with a session token. The project starts from three layers: | Layer | Where it comes from | What it holds | | --- | --- | --- | | Toolchain | `src/lib/template/vite-react.ts` | `package.json`, `index.html`, `vite.config.ts`, `tsconfig.json`, `src/main.tsx`, `src/index.css` (imports Tailwind), `src/design.css` (the colour and radius tokens) | | Skeleton | `templates/skeleton/src` | `App.tsx`, `lib/*.ts`, `components/*`, `screens/*` | | Scaffold (optional) | `templates/scaffolds//src` | Files layered on top of the skeleton for one kind of business | The UI kit is not in the project. It is imported as `@ripping/kit` and resolves to a file Ripping serves at `/kit/kit-.js`. Its API is in [The kit](kit.md). ## The identity file `src/lib/app.ts` is written once when the project is created: ```ts export const APP_ID = "__APP_ID__"; // the project id export const API = "__API__"; // the Ripping origin the app talks to ``` Every other `lib` module builds its URLs from these two. When `API` is a Ripping host, the bundler rewrites it to whichever origin is serving the app, so a preview and a published copy each talk to the origin that built them. ## The lib modules | Module | Job | Page | | --- | --- | --- | | `lib/auth.ts` | Accounts: `useAuth`, sign in, sign up, Google, reset, profile, delete | [Accounts](auth.md) | | `lib/store.ts` | Per-account data, cached in the browser and synced: `useStore`, `useSynced` | [Data](data.md) | | `lib/collections.ts` | Declares shared collections and their rule | [Data](data.md) | | `lib/shared.ts` | Records other people can see: `useShared` | [Data](data.md) | | `lib/files.ts` | Uploads: `uploadFile`, `useFiles` | [Files](files.md) | | `lib/pay.ts` | Products, checkout, purchases, subscriptions | [Payments](payments.md) | | `lib/router.ts` | Hash routes: `useRoute`, `navigate`, `match`, `Link` | [Routing and SEO](routing-and-seo.md) | | `lib/seo.ts` | `usePageTitle` | [Routing and SEO](routing-and-seo.md) | | `lib/site.ts` | `SITE`: every public-facing string and the two shape switches | [Routing and SEO](routing-and-seo.md) | | `lib/report.ts` | Crash reports and screen views | [Routing and SEO](routing-and-seo.md) | Connected services (email, models, the owner's own Stripe, Google, a REST API) have no lib module; the app posts to the `call` route directly. See [Connected services](services.md). ## The one route table `src/App.tsx` holds every screen of the app in `SCREENS`, written app-relative: ```ts const SCREENS: { path: string; render: (params: Record) => ReactNode }[] = [ { path: "/", render: () => }, { path: "/settings", render: () => }, { path: "/purchases", render: () => }, ]; ``` `:id` segments arrive in `params`. A screen renders inside ``, whose `NAV` list in `src/components/AppLayout.tsx` is the sidebar and the phone tab bar (the first five entries fit the tab bar). `PUBLIC` holds pages anyone can open signed out; the skeleton ships `/privacy` and `/terms`. Two switches in `SITE` decide the shape of the whole app: | `SITE.accounts` | `HAS_LANDING` | What `/` is | Where the app lives | | --- | --- | --- | --- | | `true` | `true` | The landing page when signed out, the app when signed in | `/` | | `true` | `false` | Sign-in when signed out, the app when signed in | `/` | | `false` | `true` | The landing page | `/app` (`APP_HOME`) | | `false` | `false` | The app | `/` | With accounts, a signed-out visitor who opens an app screen is sent to `/signin` and returned there after signing in. The first sign-in shows `` once (it sets the synced key `app.onboarded`). With no accounts there is no sign-in, no Welcome and no per-account data; what someone adds stays in their browser. `App.tsx` wraps everything in `` (toasts and confirms) and an `` that reports the crash and shows ``. ## Preview and published **Preview.** The editor bundles the project in the browser with esbuild-wasm and runs it in a sandboxed frame. Bare imports resolve to `esm.sh` (React pinned to 19.1.0; other packages take the version in `package.json`). Screen views and crash reports are not sent from the preview. **Published.** Ripping bundles the same files on the server into `index.html` and `app-.js`, uploads them to object storage under `sites//`, and serves them at `https://.`. The slug ends with the first six characters of the project id. A verified custom domain can point at the same files. `index.html` is served with `no-cache`; the script is immutable. Both copies call the same API with the same project id. The project id is compiled into the JS of every published app, so it is not a secret; everything that matters is gated by the session token. ## Where data lives | Data | Table on Ripping | Scope | Route | | --- | --- | --- | --- | | Accounts | `app_users` | Per project | `auth/` | | Per-account values (`useStore`, `useSynced`) | `app_data` | Per project, per user, per key | `data` | | Shared records (`useShared`) | `app_records` | Per project, per collection | `records/` | | Uploads | `app_files` + a private storage bucket | Per project, per owner | `files` | | Payments and subscriptions | `app_payments`, `app_subscriptions` | Per project | `call` with `provider: "pay"` | The browser also keeps a copy of per-account data under `localStorage` keys prefixed `data::` (or `data:local:` when signed out), so screens open at once and keep working offline. ## Scaffolds A scaffold is a whole starting point layered over the skeleton, chosen for a kind of business. The keys and blurbs are in `src/lib/scaffolds.ts`. | Key | Label | What it adds | | --- | --- | --- | | `app` | App | Nothing beyond the skeleton: accounts, a home screen, settings, a landing page in front. | | `lms` | Course platform | Screens for the catalogue, a course, a lesson, My learning, a certificate, and a Studio (courses, students, announcements). `lib/lms.ts` hooks (`useCourses`, `useCourseContent`, `useEnrollments`, `useProgress`, `useComments`, `useAnnouncements`, `isCreator`, `embedUrl`), `lib/types.ts`, `lib/seed.ts` sample courses shown until the creator adds real ones. Collections: `courses`, `modules`, `lessons`, `quizzes`, `questions`, `announcements` (public), `comments` (members), `enrollments` (inbox). Paid enrolment goes through `provider: "payments"` checkout with a Stripe price id on the course. The catalogue and course pages are open to signed-out visitors. | | `internal` | Internal tool | `SITE.audience: "internal"`, `landing: false`, `indexable: false`. An Activity screen and `lib/activity.ts` (`logActivity`, `useActivity`, `describe`) on a `members` collection named `activity`. `lib/roles.ts` (`isAdmin`, `useRole`), `lib/csv.ts` (`toCsv`, `downloadCsv`, `parseCsv`, `pickCsv`) with `CsvButtons`, and `lib/shortcuts.ts` (`useShortcuts`). | | `saas` | SaaS | Projects, Team and Billing screens. `lib/team.ts` (`useTeam`, `useTeamRecords`) on the `team` rule with `teams`, `team_members` and `projects` collections; invites are by email. `lib/plans.ts` (Free and Pro with limits) and `lib/billing.ts` (`useSubscription`, `usePlan`, `upgrade`, `manage`) over `provider: "payments"` on the owner's connected account. `UpgradeNudge` component. `SITE.pricing` is filled in. | | `site` | Website | `SITE.accounts: false`. About and Contact pages listed in `lib/pages.ts` (`PAGES`, `CTA_PATH`), a `SiteFrame`, `ContactForm` and `NewsletterForm`. `lib/forms.ts` `submitForm(inbox, data)` posts to the `messages` and `subscribers` inbox collections without an account; the owner gets each one by email. | | `marketplace` | Marketplace | Listed but `ready: false`: it cannot be picked and has no starter files yet. | | `extension` | Chrome extension | Not a folder under `templates/scaffolds`; it comes from `src/lib/template/extension.ts`. A Manifest V3 extension: the popup is the app (380 px wide), `src/background.ts` is the service worker, `src/content.ts` an optional content script. `src/lib/ext.ts` wraps `chrome.*` (`useStored`, `getStored`, `setStored`, `removeStored`, `activeTab`, `openTab`, `sendMessage`, `onMessage`) and works in the preview through a shim. Packaged as a zip. | A scaffold's `src/lib/collections.ts` replaces the skeleton's, so its rules are the ones Ripping enforces. --- # The kit https://docs.getripping.com/build/kit Every component, hook and helper that @ripping/kit exports, with the props that matter. ## What the kit is `@ripping/kit` is the UI every generated app imports instead of writing. It is one file (`src/kit/index.tsx` on Ripping), bundled and served at `/kit/kit-.js`; the app never installs it. Everything in it is styled with the project's design tokens (`bg-bg`, `bg-surface`, `text-ink`, `text-mute`, `bg-accent`, `text-accent-fg`, `rounded-card`, `rounded-control`, `p-gutter`), so it follows `src/design.css` and the dark class. ```ts import { ListScreen, useToast, Button } from "@ripping/kit"; ``` Wrap the app once in `` (the skeleton's `App.tsx` already does): it provides toasts and confirms to every screen. ## Screens (one call each) | Export | Props that matter | | --- | --- | | `ListScreen` | `title`, `description?`, `rows`, `columns`, `searchKeys?`, `filters?`, `fields` (the form's `FieldSpec[]`), `onCreate?(values)`, `onUpdate?(id, values)`, `onDelete?(id)`, `detail?(row) → { title, subtitle?, badge?, fields, children? }`, `itemName?`, `createLabel?`, `empty?`, `actions?`, `toolbar?`, `toForm?(row)`, `initialSort?`, `bulkActions?`, `dense?`. Table with search and filters, New opens a form in a Sheet, a row opens its detail with Edit and Delete, bulk delete with confirm, toasts. Delete is added to the bulk actions when `onDelete` is given. | | `OverviewScreen` | `title`, `description?`, `stats: { label, value, delta?, hint?, icon? }[]`, `charts?: ChartSpec[]`, `activity?: { title?, items }`, `actions?`, `children?`. Stat tiles, one or two charts, recent activity. | | `DetailScreen` | `title`, `subtitle?`, `badge?`, `fields: { label, value }[]`, `actions?`, `back?`, `onBack?`, `sections?: { title, children }[]`, `children?`. | ```ts type ChartSpec = | { title: string; type: "line"; series: { label: string; points: Point[]; color?: string }[]; format?: (n: number) => string } | { title: string; type: "bar"; data: { label: string; value: number }[]; format?: (n: number) => string }; ``` Wire a list screen to the store directly: `rows={items} onCreate={add} onUpdate={update} onDelete={remove}`. ## Shell and layout | Export | Props that matter | | --- | --- | | `AppShell` | `title`, `subtitle?`, `nav: NavItem[]`, `active`, `onNavigate(id)`, `actions?`, `footer?`, `children`. Sidebar and top bar on wide screens; header and a bottom tab bar (first 5 items, the rest behind a More button) on phones. | | `Page` | `title`, `description?`, `actions?`, `back?`, `onBack?`, `children`. Max width 6xl, gutter padding. | | `Section` | `title?`, `description?`, `actions?`, `className?`, `children`. A card with an optional header row. | | `Tabs` | `tabs: { id, label, count? }[]`, `active`, `onChange(id)`. | | `Card` | `padded?` (default `true`), `className?`. | ```ts type NavItem = { id: string; label: string; icon?: ReactNode; badge?: string | number }; ``` ## Forms and inputs | Export | Props that matter | | --- | --- | | `Button` | `variant?: "primary" \| "secondary" \| "ghost" \| "danger"`, `size?: "sm" \| "md" \| "lg"`, `loading?` (shows a spinner and disables), `icon?`, plus button attributes. `type` defaults to `"button"`. | | `IconButton` | `label` (required; becomes `aria-label` and `title`), plus button attributes. | | `Input`, `Textarea` | Native attributes. | | `Select` | `options: { value, label }[]`, `placeholder?` (rendered as an empty first option), plus select attributes. | | `Checkbox` | `label?`. Without a label it is the bare box, for wrapping in your own `