Docs
Reference

Vercel

Functions and builds authenticate as the project and environment they run in.

Functions and builds authenticate as the project and environment they run in.

What proves the identity

The token has to come from https://oidc.vercel.com/acme, and its sub claim has to match the workload you named. penv compares that claim byte for byte, with no wildcards.

Every token also has to name your workspace as its audience. Ask for this audience and no other. A token requested for two audiences fails every exchange.

What the console asks for

FieldExampleAdvancedWhat it pins
Team slugacmenoProject Settings → Security → Issuer Mode must be Team.
ProjectapinoUse the project name as Vercel lists it. A rename means a new trust.
EnvironmentproductionyesA development token carries the person who ran it. One of Production, Preview.

A field marked advanced carries a default and stays behind the disclosure until you open it.

The console also asks which project and environment this identity reaches and which role it gets, with the credential lifetime behind Advanced.

What the trust holds

Built from the example answers above:

WhatValue
Issuerhttps://oidc.vercel.com/acme
Claim comparedsub
Subjectowner:acme:project:api:environment:production
Identity namevercel-api-production

penv names the identity for you, so the form never asks for one.

The snippet the console prints

// Runs in a build or a function's request handler, never at module scope.
// JavaScript only: a custom audience has no documented HTTP form.
import { getVercelOidcToken } from '@vercel/oidc';

export async function GET() {
  const T = await getVercelOidcToken({ audience: 'YOUR_WORKSPACE_ID' });
  const r = await fetch('https://penv.cloud/api/v1/auth/oidc', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ token: T }),
  });
  process.env.PENV_TOKEN = (await r.json()).credential;
}