Skip to main content
You do not have to build a site inside Goosy. Send us a project you already have — a plain static folder, an Astro project, a Next.js project, or a Vite app with its own backend — and we build and host it for you. This page is what to prepare before you send it, and what happens once you do.
This page describes the API path: sending a project with a key, the way a developer or a coding agent would. See Pages API for the full call reference and The deploy contract for every field a project can declare.

What the archive should contain

Send a real project — package.json, your source files, and your lockfile — zipped from the project root. A few things to leave out:
  • Build output and dependency folders. node_modules, .next, dist, and any local emulator state don’t belong in the archive — they’re rebuilt for you, and including them only makes the upload slower and harder to read.
  • Your own copies and version-control files. Leave out .git and any backup or “original” copies of the project sitting alongside it.
  • Content. Your database rows and media files are not part of the code archive — see Media and Migrations for a new site below for how those travel separately.

Dependencies: no install scripts, keep the lockfile

Two things make a project buildable here, and both are worth checking before you send it:
  • No install scripts. Dependencies are installed with lifecycle scripts turned off, so a package that only finishes setting itself up through a postinstall step won’t finish setting itself up. Most everyday packages — UI libraries, routing, forms, styling — need nothing like this. If a dependency in your project does, it needs to come out before you send us the project.
  • Commit your pnpm-lock.yaml. We build with pnpm, and we install exactly what that lockfile lists — we never resolve a version off the package registry on your behalf. Keep it committed and current: run pnpm install locally after any dependency change, so the lockfile reflects what package.json asks for. We read only pnpm-lock.yaml; an npm, yarn or bun lockfile isn’t read at all, so a project that ships one of those instead — or no lockfile — is refused before anything is built, naming exactly what’s missing.
We support a defined, growing set of everyday packages for the frameworks we build — routing, UI primitives, forms, styling, and your database client among them. If your project needs something we don’t support yet, checking your project first (see the sequence below) names it for you, so you know before you spend a build finding out.

The deploy manifest

Every project we host is read through one file, pages.deploy.json — see The deploy contract for the complete field reference. It says which paths your backend answers, and what it needs to be wired to: a database, your own storage, secrets by name. You don’t have to write it. If your project doesn’t include one, we look at what you sent and write one for you, and hand it back on import so you can see what we decided and commit it if you agree. One part of it is worth knowing before you write your own: a route can be marked so that only someone signed in to your workspace can reach it — the same way /admin is commonly protected — and we enforce that before your code ever runs. See Members-only routes.

Migrations for a new site

If your project needs its own database, declare a directory of numbered SQL files in the manifest — 0000_create_table.sql, 0001_add_column.sql, and so on — and they run in that order the first time your project is built. Migrations are tracked one file at a time, not one statement at a time: if a file fails partway through, the whole file is offered again on the next build — so write every statement so it’s safe to run again from the top. create table if not exists is the simple version of that. insert or ignore only gives you the same safety when the id column itself is declared primary key or unique — a stable value alone isn’t enough, because without that declaration there’s nothing for or ignore to conflict on. Give every seeded row an explicit id of its own (never one your database assigns automatically) and declare that column primary key or unique, so a retried file skips a row it already inserted instead of adding it twice. Keep each file under 256 KB, keep the whole set to 64 files or fewer, and keep any single statement well under 100,000 bytes — batch a large seed across rows rather than one giant statement. The full list of what we check, and the number beside what you sent, is in the deploy contract’s ceilings. If you’re replacing a site you already run with us, an already-applied file has to match, byte for byte, what’s already been applied there — add new changes as new numbered files; don’t edit one that already ran. A new file you add runs against your site’s real, existing database as soon as your replacement is confirmed and built — before you ever publish (see Replacing a site you already run with us below), so review a schema change the same way you’d review any change to a live database.

Media

There are two kinds of media, and they travel differently:
  • Files your pages serve directly — images and assets your code references by path — ship inside the archive’s static folder. They’re classified as ordinary site files and go live with your first publish.
  • Files your backend reads from your own storage are different. Your storage is created for you the moment your project is imported — not held back until you publish — and it starts out empty. There’s no bulk import for existing media today. For a brand-new site, the honest path is to add files through your site’s own upload flow once it’s live, or ask us about a one-time bulk load.

Check it, then ship it

Start with the same first step either way, then the path splits depending on whether this site has a code project already. Validate first, always. Send your archive to be checked without creating anything — every problem is named in one pass, rather than one at a time across repeated attempts.

Bringing a new site

With no existing code project for this site, sending your archive creates the site and starts the build immediately — there’s no diff to review and nothing to confirm, because nothing is being replaced.
  1. Import. Creates the site and starts the build.
  2. Preview. See below — preview is live-bound, not read-only.
  3. Publish. Moves your live address to the build you just previewed — one exact version, reviewed before anything changes.

Replacing a site you already run with us

Sending an archive to a site that already has a code project never overwrites it outright — you see exactly what would change first, and nothing is written until you say so.
  1. Import — see the diff first. The response tells you nothing was written, and carries a handle together with a diff of what would change if you went ahead:
    The diff names only code-project files — never your storage, your media, your domain, or anything else that makes the site yours. Your database is the one exception, and the rule is exact: a replacement whose migrations are byte-identical to what’s already applied changes no data; a replacement that carries new migration files applies them to your live database at that same build, before you ever publish — see Migrations for a new site above.
  2. Confirm — resend the identical archive, with the handle on the URL. Send the exact same bytes to the exact same endpoint, adding the handle from step 1 as a query parameter:
    Same file, same siteId — only the query string changes. That call is the one that actually writes: it answers with an "outcome": "ingested" body and starts the build.
  3. New migrations run against your real database, at that same build — before you ever publish. If your replacement adds new migration files, this is where they run; see Migrations for a new site above for what makes a migration safe to send this way.
  4. Preview. See below.
  5. Publish. Moves your live address to the build you just previewed.

Preview is live-bound, not read-only

Your preview link is not a snapshot. The moment your project finishes its build, its database and storage are already the same ones your published site uses — there’s no separate copy — so a preview and a live site share one backend from the start. That makes preview a faithful check before you publish, but it also means a write you make while previewing is a real write: don’t submit a form, upload media, or run an admin action against a preview of a site you don’t want to change yet.

Republish to roll back

Every build you’ve shipped stays available, and putting a previous one back live is the same act as publishing — you’re just choosing an earlier build. Rolling back reaches your 100 most recent builds; an older one isn’t offered as a rollback target.

Where to go next