Skip to content
Docs

Vercel KMS SDK Reference

The @vercel/kms package wraps the KMS signing API and fetches the function's OIDC token through @vercel/oidc, so you sign without managing credentials.

signJWT and signMessage resolve the function's OIDC token at call time, which requires an active request context. Call them inside a route handler or Server Component, not at the module top level, where no request context exists.

pnpm i @vercel/kms

Signs a JWT for an issuer using its managed signing key and resolves to { token, keyId, algorithm, fingerprint }. token is the compact JWT, keyId matches the JWKS kid, algorithm is the signing key's algorithm, and fingerprint is the SHA-256 fingerprint of the signing key's public key (SHA256:<base64>). KMS sets the iat, nbf, and exp claims.

app/api/sign/route.ts
import { signJWT } from '@vercel/kms';
 
export async function GET() {
  const { token } = await signJWT({
    issuerId: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
    claims: { sub: 'user_123', scope: 'read:data' },
    ttl: 300, // seconds
  });
 
  return Response.json({ token });
}
ParameterTypeRequiredDescription
issuerIdstringYesThe ID of the issuer to sign with.
claimsRecord<string, unknown>NoCustom claims to include in the token payload.
headersRecord<string, unknown>NoAdditional headers to include in the token header.
ttlnumber | nullNoToken lifetime in seconds. Defaults to 300 seconds.
tokenstringNoAn explicit Vercel OIDC token to authenticate with. When omitted, the function's OIDC token is fetched automatically via @vercel/oidc.
regionstringNoThe KMS region to call. See Region resolution.
baseUrlstringNoOverrides the API base URL. Takes precedence over region.

signToken is deprecated in favor of signJWT. It resolves to only the compact JWT string, while signJWT also returns the signing key's keyId, algorithm, and fingerprint.

Signs a JWT for an issuer and resolves to the compact JWT string. Its options are the same as signJWT's. Use signJWT instead.

Signs an arbitrary message and resolves to { signature, keyId, algorithm, fingerprint }. signature is a Uint8Array of the raw signature bytes, keyId matches the JWKS kid, algorithm is the signing key's algorithm, and fingerprint is the SHA-256 fingerprint of the signing key's public key (SHA256:<base64>). Pass the message as a string (treated as UTF-8) or a Uint8Array of raw bytes. @vercel/kms base64-encodes it before sending. Use signJWT to mint JWTs. Message signing is rejected when the issuer's policy defines tokenClaims or the issuer defines a claims schema.

This example signs the HTTP method, URL, and body as a single message, then sends the POST with the signature in the x-signature header:

app/api/sign-message/route.ts
import { signMessage } from '@vercel/kms';
 
export async function GET() {
  const method = 'POST';
  const url = 'https://example.com/data';
  const body = JSON.stringify({ orderId: 'ord_123' });
  const message = `${method}\n${url}\n${body}`;
 
  const { signature, keyId } = await signMessage({
    issuerId: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
    message,
  });
 
  const response = await fetch(url, {
    method,
    headers: {
      'content-type': 'application/json',
      'x-signature': Buffer.from(signature).toString('base64'),
      'x-key-id': keyId,
    },
    body,
  });
 
  return Response.json({ ok: response.ok });
}

The verifier must reconstruct the same message bytes you signed. Use keyId to select the key from the issuer's JWKS after rotation, then verify the signature with that public key using crypto.verify or Web Crypto.

ParameterTypeRequiredDescription
issuerIdstringYesThe ID of the issuer to sign with.
messagestring | Uint8ArrayYesThe message to sign. A string is signed as UTF-8 bytes.
tokenstringNoAn explicit Vercel OIDC token to authenticate with. When omitted, the function's OIDC token is fetched automatically via @vercel/oidc.
regionstringNoThe KMS region to call. See Region resolution.
baseUrlstringNoOverrides the API base URL. Takes precedence over region.

The client calls the regional KMS host. It reads the region from the region option, then the VERCEL_REGION environment variable, and produces https://api-<region>.vercel.com/v1. When no region is available, it falls back to the global https://api.vercel.com/v1 host.

You do not need the SDK to sign. Send an authenticated POST to the KMS signing endpoints with any HTTP client. The signing endpoints authorize the request with a Vercel OIDC token in the Authorization: Bearer <token> header. Inside a Vercel Function, read the deployment's OIDC token with @vercel/oidc:

app/api/sign/route.ts
import { getVercelOidcToken } from '@vercel/oidc';
 
const issuerId = 'f47ac10b-58cc-4372-a567-0e02b2c3d479';
 
export async function GET() {
  const oidcToken = await getVercelOidcToken();
  const region = process.env.VERCEL_REGION;
  const baseUrl = region
    ? `https://api-${region}.vercel.com/v1`
    : 'https://api.vercel.com/v1';
 
  const response = await fetch(
    `${baseUrl}/kms/issuers/${issuerId}/sign/token`,
    {
      method: 'POST',
      headers: {
        authorization: `Bearer ${oidcToken}`,
        'content-type': 'application/json',
      },
      body: JSON.stringify({
        claims: { sub: 'user_123', scope: 'read:data' },
        ttl: 300, // seconds, defaults to 300
      }),
    },
  );
 
  const { token } = await response.json();
  return Response.json({ token });
}

The token endpoint returns { "token": "<compact JWT>" }.

To sign a message, POST to /v1/kms/issuers/<issuerId>/sign/message with a base64-encoded message. The endpoint returns { signature, keyId, algorithm }, with signature as standard-base64 of the raw signature bytes:

curl -X POST \
  "https://api.vercel.com/v1/kms/issuers/f47ac10b-58cc-4372-a567-0e02b2c3d479/sign/message" \
  -H "authorization: Bearer $VERCEL_OIDC_TOKEN" \
  -H "content-type: application/json" \
  -d "{\"message\": \"$(printf 'hello world' | base64)\"}"
Last updated September 16, 2026

Was this helpful?

supported.