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.
jiayang import vercel:expense-tracker acme/expensesWhere 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.
What it does
Section titled “What it does”- 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.
- 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.
- Reads the files the old host read too:
vercel.json,netlify.tomland.replit. - Runs the same detection as
jiayang deploy, with what the host was told about the build, and writesjiayang.jsonwith the kind it found and those settings. A project with its ownjiayang.jsonkeeps it. - Shows the plan and asks
Go ahead?. Nothing has changed until you answery. - Asks for the values of any keys the host keeps write-only. Press Enter to leave one for later.
- Makes the app, sets its environment variables and secrets, and deploys it.
- Adds the host’s scheduled jobs as the app’s schedules.
A dry run shows the plan and stops:
jiayang import vercel:expense-tracker acme/expenses --dry-runImporting 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 webRuns 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.
Where each variable goes
Section titled “Where each variable goes”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.
APIs we know
Section titled “APIs we know”| 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.
What doesn’t come along
Section titled “What doesn’t come along”| 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.
Scheduled jobs
Section titled “Scheduled jobs”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().
Options
Section titled “Options”| 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.
From the dashboard
Section titled “From the dashboard”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.
From an agent
Section titled “From an agent”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.
Next steps
Section titled “Next steps”- jiayang.json for what the import wrote.
- Secrets and outbound calls for how the keys reach their APIs.
- Share the app once it’s live.