Implementing secrets rotation
When your integration provisions resources with credentials (like API keys, database passwords, or access tokens), you must implement secrets rotation to allow Vercel users to rotate these credentials securely without reprovisioning the resource.
Vercel calls your partner API to trigger a rotation. This happens when a user or admin requests secret rotation for a resource and may also be called programmatically by Vercel. Your integration then rotates the credentials either synchronously (immediately return new secrets) or asynchronously (rotate later and notify Vercel when complete).
- The customer clicks "rotate secret" in the Vercel dashboard for a resource you manage
- Vercel makes a
POSTrequest to your/v1/installations/{installationId}/resources/{resourceId}/secrets/rotateendpoint - Your backend either generates new secrets for the resource and returns them in the response or returns
sync: falseand performs the rotation asynchronously, calling thehttps://api.vercel.com/v1/installations/{installationId}/resources/{resourceId}/secretsendpoint on Vercel to complete the rotation - Vercel updates the environment variables provided by the resource in the customer's linked projects and does not redeploy the projects. The customer must create new deployments to use the new values.
- After the period of time specified in
delayOldSecretsExpirationHours, the old secrets should stop working and be deleted by your code
Vercel calls this endpoint on your partner API to request secret rotation:
POST /v1/installations/{installationId}/resources/{resourceId}/secrets/rotate
Authorization: Bearer <oidc-token>Authentication:
Vercel includes an OIDC token in the Authorization header using either user or system authentication. You must verify this token before processing the rotation request.
When using user authentication, the token contains claims about the user who initiated the rotation, including their role (which may be ADMIN or a regular user). When using system authentication, the token represents Vercel's system making the request on behalf of an automated process.
Path parameters:
installationId: The Vercel installation ID (e.g.,icfg_9bceb8ccT32d3U417ezb5c8p)resourceId: Your external resource ID that you provided when provisioning the resource
Request body:
{
"reason": "Security audit requirement",
"delayOldSecretsExpirationHours": 3
}reason(optional): A string explaining why the rotation was requesteddelayOldSecretsExpirationHours(optional): Number of hours (0-720, max 30 days) before old secrets expire. Can be a decimal amount (ex:2.5).
Once you receive this request, rotate the secrets for this resource and keep the old secrets live for the specified time. Customers need time to create new deployments that use the new values.
For a Custom Environment, Vercel first uses the Preview-specific secret value. If no Preview-specific value exists, Vercel uses the default secret value.
You can respond in two ways depending on your implementation:
Return the rotated secrets immediately:
{
"sync": true,
"secrets": [
{
"name": "DATABASE_URL",
"value": "postgresql://user:newpass@host:5432/db"
},
{
"name": "API_KEY",
"value": "rotated-key-value"
}
],
"partial": false
}sync: true: Indicates you've completed rotation immediatelysecrets: Array of rotated secrets withnameandvalue. Each secret can also include an optionalprefixfield to namespace environment variables in connected projects.partial(optional): Set totrueif only a subset of secrets are included in the response (the default isfalseindicating your response contains the full set of environment variables for the resource)
Indicate that rotation will happen later:
{
"sync": false
}When you return sync: false, you must call Vercel's API later to complete the rotation using the Update Resource Secrets endpoint:
PUT https://api.vercel.com/v1/installations/{installationId}/resources/{resourceId}/secrets{
"secrets": [
{
"name": "DATABASE_URL",
"value": "postgresql://user:newpass@host:5432/db"
}
],
"partial": false
}Use the access token you received during installation to authenticate this request.
Here's a complete example of implementing the rotation endpoint:
import { verifyOIDCToken } from './auth';
async function handleSecretsRotation(req, res) {
const { installationId, resourceId } = req.params;
const { reason, delayOldSecretsExpirationHours = 0 } = req.body;
// Verify authentication - Vercel sends an OIDC token (user or system authentication)
const token = req.headers.authorization?.replace('Bearer ', '');
const claims = await verifyOIDCToken(token);
if (!claims || (claims.user_role && claims.user_role !== 'ADMIN')) {
return res.status(401).json({ error: 'Invalid token' });
}
// Get resource from your database
const resource = await getResource(resourceId);
if (!resource) {
return res.status(404).json({ error: 'Resource not found' });
}
// Rotate credentials in your system
const newCredentials = await rotateResourceCredentials(resourceId);
// Schedule old credentials expiration
if (delayOldSecretsExpirationHours > 0) {
await scheduleCredentialExpiration(
resource.oldCredentials,
delayOldSecretsExpirationHours
);
} else {
// Expire old credentials immediately
await expireCredentials(resource.oldCredentials);
}
// Return new secrets immediately
return res.status(200).json({
sync: true,
secrets: [
{
name: 'DATABASE_URL',
value: newCredentials.connectionString,
},
{
name: 'DATABASE_PASSWORD',
value: newCredentials.password,
},
],
partial: false
});
}Return appropriate HTTP status codes for error cases:
// Resource not found
res.status(404).json({ error: 'Resource not found' });
// Invalid request body
res.status(400).json({ error: 'Invalid delayOldSecretsExpirationHours' });
// Insufficient permissions
res.status(403).json({ error: 'User lacks permission to rotate secrets' });
// Rotation temporarily unavailable
res.status(503).json({ error: 'Rotation service unavailable, try again later' });
// Internal error during rotation
res.status(500).json({ error: 'Failed to rotate credentials' });When testing your implementation:
- Provision a test resource through your integration
- Navigate to the resource in the Vercel dashboard
- Click "Rotate Secrets" or similar action
- Verify your endpoint receives the request with correct parameters
- For synchronous rotation, confirm Vercel receives and updates the secrets
- For asynchronous rotation, verify your background job completes and calls Vercel's API
- Confirm the resource now displays the correct environment variables on the resource page in the Vercel dashboard
- Create a new deployment and confirm that the deployment uses the new values
- Confirm old credentials expire at the correct time
- Always verify authentication: Validate the OIDC token from the
Authorizationheader before processing any rotation request. Vercel uses either user or system authentication for these calls. - Validate all inputs: Check that
delayOldSecretsExpirationHoursdoesn't exceed yourmaxDelayHours - Audit all rotations: Log who or what requested rotation, when, and why (the OIDC token claims contain either user information or system authentication details)
- Handle failures gracefully: If rotation fails, maintain old credentials and return an error
- Test credential expiration: Ensure old credentials are properly revoked after the delay period
- Support partial rotation: If you can't rotate all secrets, return
partial: truewith the secrets you did rotate - Implement idempotency: Handle duplicate rotation requests gracefully
- Monitor rotation requests: Track rotation frequency to detect unusual patterns
Was this helpful?