Vercel
Vercel builds a repo and serves the result. Here that is two capabilities — /v1/platform (37) builds and runs an app, /v1/projects (27) serves a static export — and which one you want depends on whether your output needs a server.
Vercel takes a repository, builds it, and serves the output. Two capabilities answer that, and picking the right one is the whole migration:
/v1/platform(37 operations) — an app that is built and run. It has the build (POST /v1/platform/runner, listed at/v1/platform/builds), a deploy, per-deployment logs, preview, promote, rollback, env and domains. This is the like-for-like Vercel replacement./v1/projects(27 operations) — a static site. You bring a built directory; it stores it as a content-addressed release and points a domain at one. No build, because there is nothing to build.
If your project is next build with route handlers, ISR or middleware, it is an
app: use /v1/platform. If it is output: 'export' — a directory of HTML — the
Sites plane is simpler and cheaper. This documentation site is the first; the
marketing site is the second.
Start here
Register the app against your repo, then deploy it — two calls, no dashboard.
# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
-H "Authorization: Bearer $HANZO_SESSION" \
-H 'Content-Type: application/json' \
-d '{"type":"secret"}'
# 2. register the app against a git repo — it lands in draft, nothing built yet
curl -sS -X POST https://api.hanzo.ai/v1/platform/projects/default/apps \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"web","source":"git","repo":{"url":"https://github.com/acme/web","branch":"main"},"port":3000}'
# 3. build it and run it
curl -sS -X POST https://api.hanzo.ai/v1/platform/projects/default/apps/web/deploy \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"commit":"main"}'Step 2 answers 201 with the app in draft and its canonical HTTPS host already
attached; the default project needs no creating, because every org has one.
Step 3 answers 202 — an accepted deployment, not a live one — and comes back
building while BuildKit runs, which is why the logs call below exists.
Core capabilities
| Capability | What it does | Operations |
|---|---|---|
/v1/platform | Builds a repo and runs it: deploy, per-deployment logs, preview, promote, rollback, env, domains | 37 |
/v1/projects | Serves a directory you already built, as content-addressed releases with domains and purge | 27 |
/v1/functions | Publishes one handler with no app around it, and invokes it | 11 |
Nouns
Vercel is not only deploys, and neither is this. The left column is their product surface; the right is the capability that answers it.
Building and serving
| Vercel | Hanzo |
|---|---|
| Team | Your org, taken from the validated key |
| Project | Project — IAM owns it; apps and sites live under it |
| Deployment (built for you) | POST /v1/platform/projects/{project}/apps/{app}/deploy |
| Build logs | GET .../apps/{app}/deployments/{id}/logs |
| Preview deployment | POST .../apps/{app}/preview |
| Promote to production | POST .../apps/{app}/promote |
| Instant rollback | POST .../apps/{app}/rollback |
| Environment variables | PUT .../apps/{app}/env |
| Domain, and verifying it | .../apps/{app}/domains, then /{host}/verify |
| Purely static output | /v1/projects (27) — upload a directory, activate a release |
| Rollback, static | POST /v1/projects/{slug}/releases/{release}/activate |
| Edge cache purge | POST /v1/projects/{slug}/purge |
The rest of the platform
| Vercel | Hanzo |
|---|---|
| Serverless / Edge Functions | /v1/functions (11) |
| Cron Jobs | /v1/tasks (5) — a durable engine, not a timer on a deployment |
| Vercel KV | /v1/kv (6) |
| Vercel Blob | /v1/s3 (6) |
| Vercel Postgres / storage you provision | /v1/provisioning (28) |
| Edge Config | /v1/flags (8) — a definition and a deterministic decision |
| Web Analytics | /v1/event (12) |
| Speed Insights, Log Drains, traces | /v1/o11y (381) |
| Firewall, rate limiting, allowed origins | /v1/gateway (3) |
| Domains bought and held | /v1/domain (7) |
| AI SDK / AI Gateway | /v1/ai (272) — the models, not a proxy to somebody else's |
The counts are the operations each capability publishes in the document this
site is built from. /v1/ai being the largest is the point of the whole
platform rather than an add-on to hosting.
The call
Vercel:
vercel deploy --prodHanzo, as an app that builds — the shape that matches:
curl -sS -X POST \
"https://api.hanzo.ai/v1/platform/projects/acme/apps/web/deploy" \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"commit":"main"}'Then watch it, and promote when you are happy:
curl -sS "https://api.hanzo.ai/v1/platform/projects/acme/apps/web/deployments/$ID/logs" \
-H "Authorization: Bearer $HANZO_API_KEY"
curl -sS -X POST \
"https://api.hanzo.ai/v1/platform/projects/acme/apps/web/promote" \
-H "Authorization: Bearer $HANZO_API_KEY"Static instead, when the output needs no server — enqueue, upload, complete:
curl -sS -X POST https://api.hanzo.ai/v1/projects/my-site/deployments \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"commit":"'"$GIT_SHA"'"}'The bytes go straight to object storage with the grant that call hands back, so CI never holds a standing storage credential and the edge body limit never applies to your largest asset. A release is addressed by the digest of its own manifest, so rolling back is activating an earlier one — the bytes never move.
What does not carry
Short, because most of Vercel's surface has an answer above. What genuinely differs:
Git-push-to-deploy is opt-in, not ambient. Vercel watches every branch of a
connected repo and deploys what it finds. A push here reaches cloud.OnGitPush
and an app rebuilds when it tracks that repo — you say which repo an app
tracks, rather than the connection implying it for all of them.
No *.vercel.app hostname per commit. preview gives you a running
deployment; a memorable per-commit subdomain minted automatically is not part
of it. Point a domain at what you want to share.
Framework detection is Pack's job, and it is the default. Hanzo Pack is a zero-config builder: it analyses the repository, picks the provider and writes the image, for Node, Python, Go, PHP and more, with BuildKit layer and mount caching underneath. So this is not the difference it looks like — what you give up is Vercel's opinion about their framework list specifically, not detection itself.
No image optimization in the request path. Vercel rewrites and caches
images per request from next/image. Nothing here transforms an image as it is
served, so pre-size them at build.
Cron is a durable engine, not a field on a deployment. vercel.json's
crons ships with the deployment and disappears with it. /v1/tasks outlives
any one deploy, which is what you want and is a different thing to port.
On the static plane specifically: /v1/projects does not build and has no
per-request compute, and its completion manifest is a single body subject to
the edge limit — a full prerender of very many pages is what makes that bite.
Those are properties of choosing the static plane over /v1/platform, not of
the platform.
How is this guide?
Migrate
39 vendors, each mapped to the Hanzo capability that answers it — how to start, nouns, the call before and after, and what does not carry.
Netlify
Netlify serves a built directory, runs functions beside it, collects form posts and signs visitors in. Here that is /v1/projects (27) for the site, /v1/functions (11) for the code and /v1/iam (159) for the people — and a deploy is one call, because the bytes never pass through the API.