diff --git a/.changeset/docs-external-vercel-auth.md b/.changeset/docs-external-vercel-auth.md
new file mode 100644
index 0000000000..144996bf03
--- /dev/null
+++ b/.changeset/docs-external-vercel-auth.md
@@ -0,0 +1,4 @@
+---
+---
+
+Document authenticating the Vercel World from outside a deployment (local scripts and CI) with a Vercel auth token.
diff --git a/docs/content/docs/v4/api-reference/workflow-api/get-hook-by-token.mdx b/docs/content/docs/v4/api-reference/workflow-api/get-hook-by-token.mdx
index 1448c48f3e..0e18d23faf 100644
--- a/docs/content/docs/v4/api-reference/workflow-api/get-hook-by-token.mdx
+++ b/docs/content/docs/v4/api-reference/workflow-api/get-hook-by-token.mdx
@@ -122,7 +122,11 @@ import { getHookByToken, resumeHook } from "workflow/api";
export async function POST(request: Request) {
const { token, data } = await request.json();
- const expectedEnv = process.env.VERCEL_ENV || "development";
+ // On Vercel the platform sets these: `VERCEL_TARGET_ENV` carries a custom
+ // environment's slug, where `VERCEL_ENV` reports `preview`. Running against
+ // the Local World, hooks are recorded as `local`.
+ const expectedEnv =
+ process.env.VERCEL_TARGET_ENV ?? process.env.VERCEL_ENV ?? "local";
try {
const hook = await getHookByToken(token); // [!code highlight]
diff --git a/docs/content/docs/v5/api-reference/workflow-api/get-hook-by-token.mdx b/docs/content/docs/v5/api-reference/workflow-api/get-hook-by-token.mdx
index 82638ae989..0c4aee6cfc 100644
--- a/docs/content/docs/v5/api-reference/workflow-api/get-hook-by-token.mdx
+++ b/docs/content/docs/v5/api-reference/workflow-api/get-hook-by-token.mdx
@@ -124,7 +124,11 @@ import { getHookByToken, resumeHook } from "workflow/api";
export async function POST(request: Request) {
const { token, data } = await request.json();
- const expectedEnv = process.env.VERCEL_ENV || "development";
+ // On Vercel the platform sets these: `VERCEL_TARGET_ENV` carries a custom
+ // environment's slug, where `VERCEL_ENV` reports `preview`. Running against
+ // the Local World, hooks are recorded as `local`.
+ const expectedEnv =
+ process.env.VERCEL_TARGET_ENV ?? process.env.VERCEL_ENV ?? "local";
try {
const hook = await getHookByToken(token); // [!code highlight]
diff --git a/docs/content/docs/v5/configuration/cli-and-web-ui.mdx b/docs/content/docs/v5/configuration/cli-and-web-ui.mdx
index 6694e487ca..4ca56d59fe 100644
--- a/docs/content/docs/v5/configuration/cli-and-web-ui.mdx
+++ b/docs/content/docs/v5/configuration/cli-and-web-ui.mdx
@@ -12,6 +12,8 @@ The `workflow` CLI uses flags first, then environment variables, then defaults o
Vercel project and auth settings can often be inferred from `.vercel/project.json` and your Vercel CLI login.
+Whichever way they are resolved, the CLI ends up building a Vercel World from an auth token plus a project ID, team ID, and environment — the same four values any external client needs. Set them explicitly when there is no interactive `vercel login` to infer from, or when writing your own script instead of using the CLI; see [Connecting from outside a deployment](/worlds/vercel#connecting-from-outside-a-deployment).
+
## Target backend
### `--backend` / `-b`
@@ -49,7 +51,7 @@ Vercel project and auth settings can often be inferred from `.vercel/project.jso
- Environment variable: `WORKFLOW_VERCEL_ENV`
- Default: `production`
- Vercel environment for `--backend vercel`.
-- Accepts `production` or `preview`.
+- Accepts `production`, `preview`, or a custom environment's slug.
## Web UI
diff --git a/docs/content/docs/v5/configuration/worlds.mdx b/docs/content/docs/v5/configuration/worlds.mdx
index 72487e4a1f..8a43d1d8f5 100644
--- a/docs/content/docs/v5/configuration/worlds.mdx
+++ b/docs/content/docs/v5/configuration/worlds.mdx
@@ -192,6 +192,8 @@ The Vercel World is configured automatically inside Vercel deployments. The plat
Most applications should not set `WORKFLOW_VERCEL_*` variables on Vercel. They configure tooling that talks to a Vercel Workflow project from outside a deployment, such as the `workflow` CLI, the web UI, CI, or tests. The runtime warns if these variables are set in a deployed Vercel function because they do not control runtime configuration there.
+The entries below are the individual knobs. For a walkthrough of authenticating a local script or a CI job with a Vercel auth token — which variables are needed together, and how to pass them to the World factory — see [Connecting from outside a deployment](/worlds/vercel#connecting-from-outside-a-deployment).
+
Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, and `VERCEL_DEPLOYMENT_KEY` are read by the runtime inside Vercel deployments. Do not set them yourself.
### `token`
@@ -200,13 +202,14 @@ Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, an
- CLI flag: `--authToken`
- Default: inferred when possible
- Vercel API token for external tooling. Keep it secret.
+- Only meaningful alongside `projectConfig.projectId` and `projectConfig.teamId` — set all three together. Supplying both IDs but no token throws rather than failing later with a `401`.
### `projectConfig.environment`
- Environment variable: `WORKFLOW_VERCEL_ENV`
- CLI flag: `--env` or `-e`
- Default: `production`
-- Vercel environment targeted by tooling. Accepts `production` or `preview`.
+- Vercel environment targeted by tooling. Accepts `production`, `preview`, or a custom environment's **slug** — configure the slug, not the environment's ID.
### `projectConfig.projectId`
diff --git a/docs/content/worlds/v4/vercel.mdx b/docs/content/worlds/v4/vercel.mdx
index f2352126d9..83c36c42d6 100644
--- a/docs/content/worlds/v4/vercel.mdx
+++ b/docs/content/worlds/v4/vercel.mdx
@@ -28,7 +28,7 @@ That's it. Vercel automatically:
- Selects the Vercel World backend
- Configures authentication using OIDC tokens
- Provisions storage and queuing infrastructure
-- Isolates data per environment (production, preview, development)
+- Isolates data per environment (production, preview, and any custom environments)
@@ -92,6 +92,137 @@ npx workflow inspect runs \
Learn more in the [Observability](/docs/observability) documentation.
+## Connecting from outside a deployment
+
+Inside a Vercel deployment there is nothing to authenticate: the platform injects a per-request OIDC token and the Vercel World uses it automatically. A process that is *not* a deployment has no such token — a script on your laptop, a CI job driving end-to-end tests against a preview, a one-off backfill. Those clients authenticate with a **Vercel auth token** instead. Vercel checks that token against your team and project permissions, and attributes the request to the environment you name.
+
+
+ Do not reuse the `VERCEL_OIDC_TOKEN` that `vercel env pull` writes into
+ `.env.local` as your Workflow credential. It is a local artifact of your
+ linked project, not a credential for the Workflow API: requests made with it
+ are attributed to the environment carried in the token itself, not to the
+ `production` or `preview` data you mean to reach. Authenticate with a Vercel
+ auth token instead. A pulled OIDC token is still the right thing for
+ [bypassing deployment
+ protection](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/trusted-sources)
+ when your script has to fetch a protected preview URL; that is a separate
+ credential from the one that talks to the Workflow API.
+
+
+### Environment variables
+
+These four values are what an external client needs. The names are the convention shared by the `workflow` CLI, the web UI, and Workflow's own CI:
+
+| Variable | Value | Notes |
+| --- | --- | --- |
+| `WORKFLOW_VERCEL_AUTH_TOKEN` | Vercel auth token | Keep it secret. Create one under [account settings → tokens](https://vercel.com/account/tokens), scoped to the team that owns the project. |
+| `WORKFLOW_VERCEL_TEAM` | Vercel team ID (`team_...`) | Required. Set it together with the token and the project ID. |
+| `WORKFLOW_VERCEL_PROJECT` | Vercel project ID (`prj_...`) | Required. Set it together with the token and the team ID. |
+| `WORKFLOW_VERCEL_ENV` | `production`, `preview`, or a custom environment's slug | Defaults to `production`. See [`WORKFLOW_VERCEL_ENV`](#workflow_vercel_env). |
+
+Both IDs are in `.vercel/project.json` after `vercel link` — `projectId` is the project, `orgId` the team or user that owns it — or on the project's settings page.
+
+Three more are situational:
+
+- `VERCEL_DEPLOYMENT_ID` — required to **start** runs, not to read them. See [Starting runs](#starting-runs-from-outside-a-deployment).
+- `WORKFLOW_VERCEL_PROJECT_NAME` — the project slug, used only to build dashboard links.
+- `VERCEL_TOKEN` — a fallback the World reads for the Vercel API calls it makes alongside your Workflow requests (fetching a run's encryption key, resolving `deploymentId: "latest"`). A client that passes `token` explicitly, as below, already covers those calls and does not need it.
+
+For a custom environment, set `WORKFLOW_VERCEL_ENV` to the environment's **slug**, not its ID.
+
+
+ The Workflow SDK does not read `WORKFLOW_VERCEL_*` from the environment on
+ your behalf — the runtime's own `createWorld()` deliberately ignores them, so
+ that a stray variable on a deployed project cannot redirect a live
+ application. Read them in your own code and pass them to the World factory,
+ as below. Setting them on a deployed Vercel project has no effect on runtime
+ configuration, and the runtime warns when it sees them there.
+
+
+### Wiring it up in a script
+
+Build the Vercel World with an explicit config and install it with [`setWorld()`](/docs/api-reference/workflow-runtime/set-world) before the first `workflow/api` call:
+
+{/*@skip-typecheck: incomplete code sample*/}
+
+```typescript title="scripts/start-run.ts" lineNumbers
+import { start } from "workflow/api";
+import { setWorld } from "workflow/runtime";
+import { createVercelWorld } from "@workflow/world-vercel";
+import { myWorkflow } from "../workflows/my-workflow";
+
+setWorld(
+ createVercelWorld({
+ token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN, // [!code highlight]
+ projectConfig: {
+ projectId: process.env.WORKFLOW_VERCEL_PROJECT, // [!code highlight]
+ teamId: process.env.WORKFLOW_VERCEL_TEAM, // [!code highlight]
+ environment: process.env.WORKFLOW_VERCEL_ENV, // [!code highlight]
+ },
+ })
+);
+
+const run = await start(myWorkflow, ["input"]);
+console.log(run.runId);
+```
+
+Supply `token`, `projectId`, and `teamId` together. Setting the two IDs without a token fails immediately, with a message naming the missing token, rather than surfacing later as an opaque `401`.
+
+The same token authorizes the per-run key fetch that decrypts run and step data, so reads come back as values rather than opaque bytes — see [Encryption](/docs/how-it-works/encryption).
+
+This is exactly what the `workflow` CLI does under the hood. It resolves the same four values — from `--authToken` / `--project` / `--team` / `--env`, then the `WORKFLOW_VERCEL_*` variables, then `.vercel/project.json` and your `vercel login` credentials — and constructs the World with them. Passing them explicitly is the non-interactive equivalent of being logged in and linked.
+
+### Starting runs from outside a deployment
+
+[`start()`](/docs/api-reference/workflow-api/start) has to name a deployment, because a run executes the code of the deployment that created it. Inside a deployment that is the current one; outside, there is nothing to infer, so set `VERCEL_DEPLOYMENT_ID` to the deployment whose code should run the workflow:
+
+```bash
+# `vercel inspect` prints the dpl_... ID for a deployment URL
+vercel inspect https://my-app-abc123.vercel.app
+```
+
+Reads such as [`getRun()`](/docs/api-reference/workflow-api/get-run) do not need it. `start(..., { deploymentId: "latest" })` does: it resolves the newest deployment sharing that deployment's environment, so it needs one to start from.
+
+
+ Keep `WORKFLOW_VERCEL_ENV` and the environment of the deployment named by
+ `VERCEL_DEPLOYMENT_ID` aligned. The run is created in the environment you
+ authenticate as, but executes the code of the deployment you name — a run
+ whose two halves land in different environments will not execute as
+ expected.
+
+
+### In CI
+
+Wait for the deployment under test, then hand its ID to the test step alongside the four auth variables. This is how Workflow's own end-to-end suite authenticates against its preview and production deployments:
+
+```yaml title=".github/workflows/e2e.yml" lineNumbers
+jobs:
+ e2e:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Wait for the Vercel deployment
+ id: deployment
+ uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
+ with:
+ project-slug: my-app
+ environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
+
+ - name: Run end-to-end tests
+ run: pnpm test:e2e
+ env:
+ WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+ WORKFLOW_VERCEL_TEAM: team_xxxxxxxxxxxxxxxx
+ WORKFLOW_VERCEL_PROJECT: prj_xxxxxxxxxxxxxxxx
+ WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
+ VERCEL_DEPLOYMENT_ID: ${{ steps.deployment.outputs.deployment-id }}
+```
+
+Store the token as an encrypted secret and derive `WORKFLOW_VERCEL_ENV` from the branch, so a pull request run never authenticates as `production`.
+
+If the deployment is behind deployment protection, the job also needs a way to reach its URL. Grant the workflow `id-token: write` and mint a short-lived OIDC token from the runner for the `x-vercel-trusted-oidc-idp-token` header; that credential is for [Trusted Sources](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/trusted-sources) only and is separate from the Vercel auth token above.
+
## Testing & Compatibility
@@ -100,9 +231,11 @@ Learn more in the [Observability](/docs/observability) documentation.
The Vercel World requires no configuration when deployed to Vercel. For advanced use cases, you can override settings programmatically via `createVercelWorld()`.
+The `WORKFLOW_VERCEL_*` variables below configure tooling that talks to a Vercel Workflow project from outside a deployment. For the task-oriented version — which ones a local script or CI job needs, and how to hand them to the SDK — see [Connecting from outside a deployment](#connecting-from-outside-a-deployment).
+
### `WORKFLOW_VERCEL_ENV`
-The Vercel environment to use. Options: `production`, `preview`, `development`. Automatically detected.
+The Vercel environment to use. Options: `production`, `preview`, or a custom environment's slug. Default: `production`. See [Environment variables](#environment-variables) for how a custom environment is resolved.
### `WORKFLOW_VERCEL_AUTH_TOKEN`
@@ -134,6 +267,8 @@ Because it routes each run's flow invocations through a dedicated `maxConcurrenc
### Programmatic configuration
+`createVercelWorld()` accepts explicit API configuration. It does not read `WORKFLOW_VERCEL_*` automatically, so pass the environment values yourself. `projectId` and `teamId` are IDs, not slugs. To install the result as the World your app uses, see [Wiring it up in a script](#wiring-it-up-in-a-script).
+
{/*@skip-typecheck: incomplete code sample*/}
```typescript title="workflow.config.ts" lineNumbers
@@ -141,10 +276,9 @@ import { createVercelWorld } from "@workflow/world-vercel";
const world = createVercelWorld({
token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN,
- baseUrl: "https://api.vercel.com/v1/workflow",
projectConfig: {
- projectId: "my-project",
- teamId: "my-team",
+ projectId: "prj_...",
+ teamId: "team_...",
environment: "production",
},
});
@@ -197,6 +331,6 @@ The Vercel World uses Vercel's infrastructure for workflow execution:
- **Storage** - Workflow data is stored in Vercel's cloud with automatic replication and [end-to-end encryption](/docs/how-it-works/encryption)
- **Queuing** - Steps are distributed across serverless functions via [Vercel Queues](https://vercel.com/docs/queues) with automatic retries and [consumer function security](#consumer-function-security)
-- **Authentication** - OIDC tokens provide secure, automatic authentication
+- **Authentication** - OIDC tokens provide secure, automatic authentication inside deployments; clients outside one authenticate with a [Vercel auth token](#connecting-from-outside-a-deployment)
For more details, see the [Vercel Workflow documentation](https://vercel.com/docs/workflows).
diff --git a/docs/content/worlds/v5/vercel.mdx b/docs/content/worlds/v5/vercel.mdx
index ac96611222..3056106f42 100644
--- a/docs/content/worlds/v5/vercel.mdx
+++ b/docs/content/worlds/v5/vercel.mdx
@@ -28,7 +28,7 @@ That's it. Vercel automatically:
- Selects the Vercel World backend
- Configures authentication using OIDC tokens
- Provisions storage and queuing infrastructure
-- Isolates data per environment (production, preview, development)
+- Isolates data per environment (production, preview, and any custom environments)
@@ -128,6 +128,139 @@ npx workflow inspect runs \
Learn more in the [Observability](/docs/observability) documentation.
+## Connecting from outside a deployment
+
+Inside a Vercel deployment there is nothing to authenticate: the platform injects a per-request OIDC token and the Vercel World uses it automatically. A process that is *not* a deployment has no such token — a script on your laptop, a CI job driving end-to-end tests against a preview, a one-off backfill. Those clients authenticate with a **Vercel auth token** instead. Vercel checks that token against your team and project permissions, and attributes the request to the environment you name.
+
+
+ Do not reuse the `VERCEL_OIDC_TOKEN` that `vercel env pull` writes into
+ `.env.local` as your Workflow credential. It is a local artifact of your
+ linked project, not a credential for the Workflow API: requests made with it
+ are attributed to the environment carried in the token itself, not to the
+ `production` or `preview` data you mean to reach. Authenticate with a Vercel
+ auth token instead. A pulled OIDC token is still the right thing for
+ [bypassing deployment
+ protection](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/trusted-sources)
+ when your script has to fetch a protected preview URL; that is a separate
+ credential from the one that talks to the Workflow API.
+
+
+### Environment variables
+
+These four values are what an external client needs. The names are the convention shared by the `workflow` CLI, the web UI, and Workflow's own CI:
+
+| Variable | Value | Notes |
+| --- | --- | --- |
+| `WORKFLOW_VERCEL_AUTH_TOKEN` | Vercel auth token | Keep it secret. Create one under [account settings → tokens](https://vercel.com/account/tokens), scoped to the team that owns the project. |
+| `WORKFLOW_VERCEL_TEAM` | Vercel team ID (`team_...`) | Required. Set it together with the token and the project ID. |
+| `WORKFLOW_VERCEL_PROJECT` | Vercel project ID (`prj_...`) | Required. Set it together with the token and the team ID. |
+| `WORKFLOW_VERCEL_ENV` | `production`, `preview`, or a custom environment's slug | Defaults to `production`. See [`WORKFLOW_VERCEL_ENV`](#workflow_vercel_env). |
+
+Both IDs are in `.vercel/project.json` after `vercel link` — `projectId` is the project, `orgId` the team or user that owns it — or on the project's settings page.
+
+Three more are situational:
+
+- `VERCEL_DEPLOYMENT_ID` — required to **start** runs, not to read them. See [Starting runs](#starting-runs-from-outside-a-deployment).
+- `WORKFLOW_VERCEL_PROJECT_NAME` — the project slug, used only to build dashboard links.
+- `VERCEL_TOKEN` — a fallback the World reads for the Vercel API calls it makes alongside your Workflow requests (fetching a run's encryption key, resolving `deploymentId: "latest"`). A client that passes `token` explicitly, as below, already covers those calls and does not need it.
+
+For a custom environment, set `WORKFLOW_VERCEL_ENV` to the environment's **slug**, not its ID — the slug is what the consuming deployment compares against.
+
+
+ The Workflow SDK does not read `WORKFLOW_VERCEL_*` from the environment on
+ your behalf — the runtime's own `createWorld()` deliberately ignores them, so
+ that a stray variable on a deployed project cannot redirect a live
+ application. Read them in your own code and pass them to the World factory,
+ as below. Setting them on a deployed Vercel project has no effect on runtime
+ configuration, and the runtime warns when it sees them there.
+
+
+### Wiring it up in a script
+
+Build the Vercel World with an explicit config and install it with [`setWorld()`](/docs/api-reference/workflow-runtime/set-world) before the first `workflow/api` call:
+
+{/*@skip-typecheck: incomplete code sample*/}
+
+```typescript title="scripts/start-run.ts" lineNumbers
+import { start } from "workflow/api";
+import { setWorld } from "workflow/runtime";
+import { createWorld } from "@workflow/world-vercel";
+import { myWorkflow } from "../workflows/my-workflow";
+
+setWorld(
+ createWorld({
+ token: process.env.WORKFLOW_VERCEL_AUTH_TOKEN, // [!code highlight]
+ projectConfig: {
+ projectId: process.env.WORKFLOW_VERCEL_PROJECT, // [!code highlight]
+ teamId: process.env.WORKFLOW_VERCEL_TEAM, // [!code highlight]
+ environment: process.env.WORKFLOW_VERCEL_ENV, // [!code highlight]
+ },
+ })
+);
+
+const run = await start(myWorkflow, ["input"]);
+console.log(run.runId);
+```
+
+Supply `token`, `projectId`, and `teamId` together. Setting the two IDs without a token fails immediately, with a message naming the missing token, rather than surfacing later as an opaque `401`.
+
+The same token authorizes the per-run key fetch that decrypts run and step data, so reads come back as values rather than opaque bytes — see [Encryption](/docs/how-it-works/encryption).
+
+This is exactly what the `workflow` CLI does under the hood. It resolves the same four values — from `--authToken` / `--project` / `--team` / `--env`, then the `WORKFLOW_VERCEL_*` variables, then `.vercel/project.json` and your `vercel login` credentials — and constructs the World with them. Passing them explicitly is the non-interactive equivalent of being logged in and linked.
+
+### Starting runs from outside a deployment
+
+[`start()`](/docs/api-reference/workflow-api/start) has to name a deployment, because a run executes the code of the deployment that created it. Inside a deployment that is the current one; outside, there is nothing to infer, so set `VERCEL_DEPLOYMENT_ID` to the deployment whose code should run the workflow:
+
+```bash
+# `vercel inspect` prints the dpl_... ID for a deployment URL
+vercel inspect https://my-app-abc123.vercel.app
+```
+
+Reads such as [`getRun()`](/docs/api-reference/workflow-api/get-run) do not need it. `start(..., { deploymentId: "latest" })` does: it resolves the newest deployment sharing that deployment's environment, so it needs one to start from.
+
+
+ The environment you authenticate as and the deployment you target must
+ agree. A client that creates a run in `preview` but points it at a
+ production deployment creates a run that never executes: environments are
+ isolated from each other, so a deployment refuses work created in a
+ different one. If a run stays `pending` and the deployment logs a refusal
+ naming two environments, check `WORKFLOW_VERCEL_ENV` against the environment
+ of the deployment in `VERCEL_DEPLOYMENT_ID`.
+
+
+### In CI
+
+Wait for the deployment under test, then hand its ID to the test step alongside the four auth variables. This is how Workflow's own end-to-end suite authenticates against its preview and production deployments:
+
+```yaml title=".github/workflows/e2e.yml" lineNumbers
+jobs:
+ e2e:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Wait for the Vercel deployment
+ id: deployment
+ uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
+ with:
+ project-slug: my-app
+ environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
+
+ - name: Run end-to-end tests
+ run: pnpm test:e2e
+ env:
+ WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+ WORKFLOW_VERCEL_TEAM: team_xxxxxxxxxxxxxxxx
+ WORKFLOW_VERCEL_PROJECT: prj_xxxxxxxxxxxxxxxx
+ WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
+ VERCEL_DEPLOYMENT_ID: ${{ steps.deployment.outputs.deployment-id }}
+```
+
+Store the token as an encrypted secret and derive `WORKFLOW_VERCEL_ENV` from the branch, so a pull request run never authenticates as `production`.
+
+If the deployment is behind deployment protection, the job also needs a way to reach its URL. Grant the workflow `id-token: write` and mint a short-lived OIDC token from the runner for the `x-vercel-trusted-oidc-idp-token` header; that credential is for [Trusted Sources](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/trusted-sources) only and is separate from the Vercel auth token above.
+
## Testing & Compatibility
@@ -142,9 +275,11 @@ Most users never need to set the `WORKFLOW_VERCEL_*` variables below. They are o
For example, you might set them when running `workflow inspect runs --backend vercel` from CI without an interactive `vercel login`, or when running tests against a specific preview deployment. In normal local development, the CLI infers these values from your `.vercel` directory and Vercel CLI login. In a deployed Vercel function, these variables have no effect on runtime configuration, and the runtime warns if they are set there.
+For the task-oriented version of this — which variables a local script or CI job needs, and how to hand them to the SDK — see [Connecting from outside a deployment](#connecting-from-outside-a-deployment).
+
### `WORKFLOW_VERCEL_ENV`
-The Vercel environment to target. Options: `production`, `preview`. Default: `production`.
+The Vercel environment to target. Options: `production`, `preview`, or a custom environment's slug. Default: `production`. See [Environment variables](#environment-variables) for how a custom environment is resolved.
### `WORKFLOW_VERCEL_AUTH_TOKEN`
@@ -216,7 +351,7 @@ The WebSocket handshake is itself a span, `workflow.events.ws.connect`, so the c
### Programmatic configuration
-`createWorld()` accepts explicit API configuration. It does not read `WORKFLOW_VERCEL_*` automatically, so pass the environment values yourself when you want a configured World module:
+`createWorld()` accepts explicit API configuration. It does not read `WORKFLOW_VERCEL_*` automatically, so pass the environment values yourself when you want a configured World module. To configure a client at the point of use instead of through `WORKFLOW_TARGET_WORLD`, see [Wiring it up in a script](#wiring-it-up-in-a-script).
{/*@skip-typecheck: incomplete code sample*/}
@@ -284,6 +419,6 @@ The Vercel World uses Vercel's infrastructure for workflow execution:
- **Storage** - Workflow data is stored in Vercel's cloud with automatic replication and [end-to-end encryption](/docs/how-it-works/encryption)
- **Queuing** - Steps are distributed across serverless functions via [Vercel Queues](https://vercel.com/docs/queues) with automatic retries and [consumer function security](#consumer-function-security)
-- **Authentication** - OIDC tokens provide secure, automatic authentication
+- **Authentication** - OIDC tokens provide secure, automatic authentication inside deployments; clients outside one authenticate with a [Vercel auth token](#connecting-from-outside-a-deployment)
For more details, see the [Vercel Workflow documentation](https://vercel.com/docs/workflows).