Skip to content

Import from another host

jiayang import makes a new app from a project that runs somewhere else. It reads the project’s settings and production variables, gets the code, writes a jiayang.json, makes the app, puts each variable where it belongs here, deploys it and adds its scheduled jobs. It shows you all of that first and asks before it changes anything.

Terminal window
jiayang import vercel:expense-tracker acme/expenses

Where the project is now goes first:

From Write Guide
Vercel vercel:<project> Import from Vercel
Netlify netlify:<site> Import from Netlify
Replit replit, with the Repl’s folder as the third argument Import from Replit
Lovable, Bolt, v0 or any repository the repository’s address, or the folder with the code Import from a repository

The app is the second argument, as <workspace>/<app>. It mustn’t exist yet: an import never changes an app that’s already there.

  1. Reads the project from the host’s API, for Vercel and Netlify, with a token you paste. The token is used for these requests and never saved.
  2. Gets the code. For Vercel and Netlify it clones the repository the project is linked to, with your own git, into a new directory named after the project, or the directory you give as the third argument. If that directory already has the code, it uses it as it is.
  3. Reads the files the old host read too: vercel.json, netlify.toml and .replit.
  4. Runs the same detection as jiayang deploy, with what the host was told about the build, and writes jiayang.json with the kind it found and those settings. A project with its own jiayang.json keeps it.
  5. Shows the plan and asks Go ahead?. Nothing has changed until you answer y.
  6. Asks for the values of any keys the host keeps write-only. Press Enter to leave one for later.
  7. Makes the app, sets its environment variables and secrets, and deploys it.
  8. Adds the host’s scheduled jobs as the app’s schedules.

A dry run shows the plan and stops:

Terminal window
jiayang import vercel:expense-tracker acme/expenses --dry-run
Importing expense-tracker from Vercel into acme/expenses.
Code: https://github.com/acme-finance/expense-tracker.git (main), cloned into expense-tracker, with the app in web
Runs as: a static site, served by Workers (Vite)
jiayang.json to write:
{
"version": 1,
"kind": "static",
"build": {
"output": "build"
}
}
Environment variables (2)
REGION
VITE_API_BASE
Secrets (2)
OPENAI_API_KEY for OpenAI, added to requests to api.openai.com, Vercel keeps it write-only, so its value has to come from you
STRIPE_SECRET_KEY for Stripe, added to requests to api.stripe.com
Left out (1)
CRON_SECRET: Vercel sent it with each scheduled call. Our scheduler signs its calls instead: check them with requireUser().
Not set, for you to decide (1)
DATABASE_URL: It connects to a database with a password, and apps here hold no credentials. If it's Postgres, a workspace owner connects it with `jiayang postgres add` and gives it to the app with `jiayang postgres grant`, and the app sends its SQL over HTTP without the password (see /docs/postgres/). That needs Team or Business: Free has none. Or move the data to the app's own database (env.DB, see /docs/database/).
Schedules, added once it's deployed (1)
GET /api/cron/digest at "0 7 * * 1-5" (UTC)
That was a dry run, so nothing was made, written or deployed.

A dry run clones into a temporary directory and deletes it, and writes nothing in yours.

Only production’s values come over: a value for previews, a branch or development stays behind.

What it is What happens
Plain configuration, like REGION or API_BASE_URL An environment variable, with the same value.
A value the build puts in the browser’s code: NEXT_PUBLIC_, VITE_, PUBLIC_, REACT_APP_, NUXT_PUBLIC_, EXPO_PUBLIC_, GATSBY_ or VUE_APP_ An environment variable. Anyone who can open the app could read it on the old host too.
A key for an API we know, by its name or by how it starts A secret for that API’s host. The app keeps an environment variable of the same name holding set-by-jiayang, so an SDK that won’t start without a key still starts. Our proxy replaces it with the real key on each request to that host.
A value the old host keeps secret: Vercel’s Secret variables, Netlify’s with secret values A secret, for an API we know. Any other is not set, and the plan says how to store it. It never becomes a plain variable.
A value over 4,096 bytes, or over more than one line, like a pretty-printed JSON config or a certificate Not set: a variable here holds one line of up to 4,096 bytes, a secret one line of up to 8,192. Put it in a file in the app’s code, or set it yourself.
Something the old host or we set, like VERCEL_URL, NETLIFY, REPL_ID or PORT Left out.
Anything the app would have to hold itself, like a database password or a session signing key Not set. The plan says why, and what to do instead.

A secret key in a browser variable, like NEXT_PUBLIC_STRIPE_KEY=sk_live_..., is not set. It was readable by everyone on the old host, so rotate it with the provider and call the API from the server side.

An import sets at most 100 variables and secrets. Past that, in name order, the rest are left for you to set. A project with more than 200 isn’t imported: move it by hand.

API Variable names Sent to Header
Stripe STRIPE_SECRET_KEY, STRIPE_API_KEY, STRIPE_KEY, or a value starting sk_live_, sk_test_, rk_live_ or rk_test_ api.stripe.com Authorization: Bearer
OpenAI OPENAI_API_KEY, or sk-proj- api.openai.com Authorization: Bearer
Anthropic ANTHROPIC_API_KEY, or sk-ant- api.anthropic.com x-api-key
OpenRouter OPENROUTER_API_KEY, or sk-or- openrouter.ai Authorization: Bearer
Google Gemini GEMINI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY generativelanguage.googleapis.com x-goog-api-key
Groq, Mistral, xAI, Perplexity, Replicate their _API_KEY names, or gsk_, xai-, pplx-, r8_ their API hosts Authorization: Bearer
ElevenLabs ELEVENLABS_API_KEY api.elevenlabs.io xi-api-key
Resend, SendGrid RESEND_API_KEY, SENDGRID_API_KEY or SG. api.resend.com, api.sendgrid.com Authorization: Bearer
Postmark POSTMARK_SERVER_TOKEN api.postmarkapp.com X-Postmark-Server-Token
GitHub, Slack, Notion, Airtable, Linear, DigitalOcean GITHUB_TOKEN, SLACK_BOT_TOKEN, NOTION_TOKEN, AIRTABLE_API_KEY, LINEAR_API_KEY, DIGITALOCEAN_TOKEN, or ghp_, xoxb-, ntn_, lin_api_, dop_v1_ their API hosts Authorization
Upstash Redis and Vercel KV UPSTASH_REDIS_REST_TOKEN, KV_REST_API_TOKEN the host in UPSTASH_REDIS_REST_URL or KV_REST_API_URL Authorization: Bearer

Each provider’s API reference gives the host and header we use (checked 7 October 2026). A key for any other API is left for you to store with jiayang secret set, naming its host. To use the workspace’s own Anthropic or OpenAI key instead of the app’s, see Company AI keys.

On the old host Here
A Postgres URL with a password (DATABASE_URL, POSTGRES_URL, Neon’s or Supabase’s) A workspace owner connects it and gives it to the app, which sends its SQL over HTTP and never holds the password. Team and Business have connections; Free has none. Or move the data to the app’s own database.
Another database URL with a password (MONGODB_URI, MYSQL_URL) Move the data to the app’s own database, or reach yours through an HTTPS API with a key stored as a secret.
Redis over its own protocol (REDIS_URL, KV_URL) Use the provider’s REST API: Upstash’s URL and token come over as a variable and a secret.
Vercel Blob, Netlify Blobs, Replit Object Storage The app’s own file storage.
Vercel KV The app’s own key-value storage, with "kv": true in jiayang.json, on Team or Business.
Vercel Postgres A Postgres connection, as for any Postgres URL above.
Vercel Edge Config, Replit Database Environment variables, or the app’s database.
Serverless functions: Vercel’s api/ folder, Netlify Functions and Edge Functions Serve those paths from a Worker or a container’s server.
Its own sign-in: NextAuth, Better Auth, Netlify Identity, Replit Auth Not needed. Everyone signs in through us before the app, and an SDK’s requireUser() says who.
A webhook signing secret (STRIPE_WEBHOOK_SECRET, whsec_...) A verifier on a public path, which checks each delivery before it reaches the app.
Redirects and headers in vercel.json or netlify.toml _redirects and _headers files in the site’s output, as for any static site. The plan prints netlify.toml’s redirects as _redirects lines.
Netlify Forms A Worker or server that takes the post, or a form service with its key as a secret.
Deployment protection and passwords Nothing to do. An app here opens only for the people you share it with.
Analytics scripts that report to the old host Remove them. The app’s Activity tab shows who used it.

Each of these is listed under Not carried over, or under Not set, for you to decide for a variable, with the same advice.

Vercel’s cron jobs, from the project or its vercel.json, become the app’s schedules, called with GET at the same times once the app is deployed. A schedule written with a name, like @daily, isn’t added: write it as five cron fields with jiayang schedule add. Vercel sent CRON_SECRET with each call. Our scheduler signs its calls instead, so check them with requireUser().

Option What it does
--dry-run Shows the plan and stops. Nothing is made, written or deployed.
--no-deploy Makes the app and sets its variables and secrets, without deploying it.
--yes Goes ahead without asking, and asks for no values. Needed where there’s no terminal, as in CI.
--env-file <FILE> Values from a .env file, for keys the host keeps write-only, or Replit’s Secrets. It’s read on your machine and nowhere else.
--team <TEAM> The Vercel team a project belongs to, by its id or slug.
--kind <KIND> static, worker or container, when the code could be more than one.
--json The result as JSON: the app, its URL and version, the names it set, and what’s left.

If the deploy fails, the app stays, with its variables and secrets. Fix what the error says, then run jiayang deploy from the app’s directory. If anything before that fails, nothing that was made is undone either: the message says what was done.

In the workspace’s Apps page, select Import. For Vercel and Netlify, name the project and paste a token. We read the project with it once and keep it nowhere. The review shows each variable and where it goes, with a box for each key the host keeps write-only. Plain values are shown; a key’s value never comes back to the browser. Then Create app and import makes the app and sets everything up.

The dashboard can’t build the code, so the last step gives you the jiayang.json to add to the project and the commands for the first deploy. If the project is on GitHub, you can deploy it on every push with GitHub Actions from the app’s Deploy tab.

A lookup can be used for 30 minutes. For Replit and other repositories, the dialog gives the command to run in a terminal, since those need the code.

Only someone who can make apps in the workspace can import. Each import is in the audit log as app.imported, with the project and the names of what was set, never a value.

An agent with our MCP server can call import_app with a repository, a folder or a project, then deploy it with deploy_app. A folder, or a repository on your machine (a file:// address), has to be inside the directories you gave the agent’s session, and an ssh address to your own machine is refused. It never asks you for a token or a key in the conversation. To import from Vercel or Netlify that way, set VERCEL_TOKEN or NETLIFY_AUTH_TOKEN where the server runs, or run jiayang import in your own terminal.