Vercel2 additive
2 new response fields #
- permissions.schedulePreview
- projects[].permissions.schedulePreview
APIs / Vercel
Breaking changes, deprecations and new fields in the Vercel API, as they ship.
Producer-side tools call this additive. It breaks exhaustive switches, strict parsers and generated client enums.
can now return "organization-team-dsync-update"
switch (response.name) {
// existing branches …
// no branch for "organization-team-dsync-update"
default:
throw new Error("unexpected name");
}
can now return "aws-oidc"
switch (response.type) {
// existing branches …
// no branch for "aws-oidc"
default:
throw new Error("unexpected type");
}
An unhandled branch in your error handling.
can now return "ai-security-review", "firewall-conditional-bypass-updated", "integration-previous-secret-revoked", "integration-secret-rotated", "team-runtime-cache-updated", "vercel-agent-security-finding-created"
switch (response.name) {
// existing branches …
// no branch for "ai-security-review"
// no branch for "firewall-conditional-bypass-updated"
// no branch for "integration-previous-secret-revoked"
// no branch for "integration-secret-rotated"
// no branch for "team-runtime-cache-updated"
// no branch for "vercel-agent-security-finding-created"
default:
throw new Error("unexpected name");
}
switch (response.source) {
// existing branches …
// no branch for "project-overview-recommended-integrations"
default:
throw new Error("unexpected source");
}
Reads of this field return undefined, and the failure surfaces wherever that value is used rather than at the call.
// response.transferRequest is now undefined
const transferRequest = response.transferRequest;
No action needed.
can now return "org-admin-sync"
switch (response.membership.joinedFrom.origin) {
// existing branches …
// no branch for "org-admin-sync"
default:
throw new Error("unexpected origin");
}
can now return "team-data-cache-user-disabled-updated"
switch (response.replacedBy[0]) {
// existing branches …
// no branch for "team-data-cache-user-disabled-updated"
default:
throw new Error("unexpected replacedBy");
}
can now return "connect-branding-updated"
switch (response.name) {
// existing branches …
// no branch for "connect-branding-updated"
default:
throw new Error("unexpected name");
}
can now return "14cpu-28gb"
switch (response.resourceConfig.buildMachineType) {
// existing branches …
// no branch for "14cpu-28gb"
default:
throw new Error("unexpected buildMachineType");
}
can now return "read-write:ai-gateway-byok-credentials", "read:ai-gateway-byok-credentials"
switch (response.payload.permissions[0]) {
// existing branches …
// no branch for "read-write:ai-gateway-byok-credentials"
// no branch for "read:ai-gateway-byok-credentials"
default:
throw new Error("unexpected permissions");
}
No action needed unless it is required.
// response.projects[0].resourceConfig.elasticBuildMachine.memory is now undefined
const memory = response.projects[0].resourceConfig.elasticBuildMachine.memory;
// response.defaultResourceConfig.elasticBuildMachine.label is now undefined
const label = response.defaultResourceConfig.elasticBuildMachine.label;
// response.projects[0].defaultResourceConfig.elasticBuildMachine.cores is now undefined
const cores = response.projects[0].defaultResourceConfig.elasticBuildMachine.cores;
Dead branch, or an undocumented response you still receive.
// response.v0 is now undefined
const v0 = response.v0;
can now return "project-git-credential-attestation-created", "project-git-credential-attestation-deleted", "project-git-credential-grant-attestation-revoked", "project-git-credential-grant-attested"
switch (response.name) {
// existing branches …
// no branch for "project-git-credential-attestation-created"
// no branch for "project-git-credential-attestation-deleted"
// no branch for "project-git-credential-grant-attestation-revoked"
// no branch for "project-git-credential-grant-attested"
default:
throw new Error("unexpected name");
}
can now return "project"
switch (response.creator.type) {
// existing branches …
// no branch for "project"
default:
throw new Error("unexpected type");
}
can now return "messageboard-project-channel-created", "messageboard-project-private-channel-created"
switch (response.name) {
// existing branches …
// no branch for "messageboard-project-channel-created"
// no branch for "messageboard-project-private-channel-created"
default:
throw new Error("unexpected name");
}
can now return "messageboard-private-project-granted", "messageboard-private-project-revoked", "messageboard-project-granted", "messageboard-project-revoked"
switch (response.name) {
// existing branches …
// no branch for "messageboard-private-project-granted"
// no branch for "messageboard-private-project-revoked"
// no branch for "messageboard-project-granted"
// no branch for "messageboard-project-revoked"
default:
throw new Error("unexpected name");
}
can now return "startup-terms-accepted"
switch (response.replacedBy[0]) {
// existing branches …
// no branch for "startup-terms-accepted"
default:
throw new Error("unexpected replacedBy");
}
A relaxation. Existing payloads are unaffected.
can now return "organization-sso-deleted"
switch (response.name) {
// existing branches …
// no branch for "organization-sso-deleted"
default:
throw new Error("unexpected name");
}
js
// response.resourceConfig.buildMachine.cores is now undefined
const cores = response.resourceConfig.buildMachine.cores;
switch (response.config.resourceConfig.buildMachine.purchaseType) {
// existing branches …
// no branch for "14cpu-28gb"
default:
throw new Error("unexpected purchaseType");
}
can now return "read-write:webhooks"
switch (response.payload.permissions[0]) {
// existing branches …
// no branch for "read-write:webhooks"
default:
throw new Error("unexpected permissions");
}
js
// response.daemons[0].config.replicas is now undefined
const replicas = response.daemons[0].config.replicas;
js
// response.daemons[0].config.configuredReplicas is now undefined
const configuredReplicas = response.daemons[0].config.configuredReplicas;
js
// response.projects[0].permissions.messageboardSpace is now undefined
const messageboardSpace = response.projects[0].permissions.messageboardSpace;
can now return "product"
switch (response.payload.budget.budgetItem.scope) {
// existing branches …
// no branch for "product"
default:
throw new Error("unexpected scope");
}
can now return "messageboard-channel-created", "messageboard-channel-updated", "messageboard-private-channel-created", "messageboard-private-channel-updated"
switch (response.replacedBy[0]) {
// existing branches …
// no branch for "messageboard-channel-created"
// no branch for "messageboard-channel-updated"
// no branch for "messageboard-private-channel-created"
// no branch for "messageboard-private-channel-updated"
default:
throw new Error("unexpected replacedBy");
}
js
// response.ownerId is now undefined
const ownerId = response.ownerId;
js
// response.payload.spaceId is now undefined
const spaceId = response.payload.spaceId;
can now return "xaa-updated"
switch (response.name) {
// existing branches …
// no branch for "xaa-updated"
default:
throw new Error("unexpected name");
}
js
// response.daemons[0].config.image.vhs is now undefined
const vhs = response.daemons[0].config.image.vhs;
js
// response.daemons[0].replicas.items[0].iroh is now undefined
const iroh = response.daemons[0].replicas.items[0].iroh;
can now return "use:github"
switch (response.payload.permissions[0]) {
// existing branches …
// no branch for "use:github"
default:
throw new Error("unexpected permissions");
}
js
// response.payload.data.reason is now undefined
const reason = response.payload.data.reason;
js
// response.payload.data.planSlug is now undefined
const planSlug = response.payload.data.planSlug;
No error is raised. The value is ignored and your results change silently, which is the hardest class of break to notice.
await client.post("/v2/sandboxes/sessions/{sessionId}/cmd", {
// cmdId is now ignored, not rejected
cmdId: value,
});
can now return "connect-cancel-mcp-event-subscription", "connect-create-mcp-event-subscription", "organization-name-update"
switch (response.type) {
// existing branches …
// no branch for "connect-cancel-mcp-event-subscription"
// no branch for "connect-create-mcp-event-subscription"
// no branch for "organization-name-update"
default:
throw new Error("unexpected type");
}
can now return "read-write:access-group"
switch (response.payload.nextPermissions[0]) {
// existing branches …
// no branch for "read-write:access-group"
default:
throw new Error("unexpected nextPermissions");
}
Producer-side tools call this non-breaking. For a consumer it is a null dereference on an unknown future date.
// response.payload.author may now be absent or null
const author = response.payload.author ?? fallback;
can now return "26.x"
switch (response.projects[0].nodeVersion) {
// existing branches …
// no branch for "26.x"
default:
throw new Error("unexpected nodeVersion");
}
js
// response.result is now undefined
const result = response.result;
js
// response.pagination is now undefined
const pagination = response.pagination;
can now return "firewall-conditional-bypass-created", "firewall-conditional-bypass-deleted"
switch (response.name) {
// existing branches …
// no branch for "firewall-conditional-bypass-created"
// no branch for "firewall-conditional-bypass-deleted"
default:
throw new Error("unexpected name");
}
No action needed unless you validate responses against a closed schema.
can now return "response_code"
switch (response.active.rulesets[0].conditionGroup[0].conditions[0].type) {
// existing branches …
// no branch for "response_code"
default:
throw new Error("unexpected type");
}
can now return "read-write:project-members", "read:audit-log", "read:project-members"
switch (response.payload.permissions[0]) {
// existing branches …
// no branch for "read-write:project-members"
// no branch for "read:audit-log"
// no branch for "read:project-members"
default:
throw new Error("unexpected permissions");
}
can now return "read-write:ai-gateway-budgets", "read:ai-gateway-budgets"
switch (response.payload.after.permissions[0]) {
// existing branches …
// no branch for "read-write:ai-gateway-budgets"
// no branch for "read:ai-gateway-budgets"
default:
throw new Error("unexpected permissions");
}
// response.services[0].builder.config.daemon.replicas may now be absent or null
const replicas = response.services[0].builder.config.daemon.replicas ?? fallback;
Usually ignored rather than rejected, so the call succeeds and does something subtly different.
await client.post("/v9/projects/{idOrName}", {
// protectionMode is now ignored, not rejected
protectionMode: value,
});
can now return "admin-saml-sp-signing-key-created", "admin-saml-sp-signing-key-updated"
switch (response.name) {
// existing branches …
// no branch for "admin-saml-sp-signing-key-created"
// no branch for "admin-saml-sp-signing-key-updated"
default:
throw new Error("unexpected name");
}
can now return "organization-dsync-sync-start"
switch (response.name) {
// existing branches …
// no branch for "organization-dsync-sync-start"
default:
throw new Error("unexpected name");
}
can now return "admin-saml-idp-material-deleted", "admin-saml-idp-material-set", "vercel-ci-repository-settings-updated"
switch (response.name) {
// existing branches …
// no branch for "admin-saml-idp-material-deleted"
// no branch for "admin-saml-idp-material-set"
// no branch for "vercel-ci-repository-settings-updated"
default:
throw new Error("unexpected name");
}
can now return "stripe-api-key"
switch (response.type) {
// existing branches …
// no branch for "stripe-api-key"
default:
throw new Error("unexpected type");
}
can now return "sandbox-drive-snapshot-created", "sandbox-drive-snapshot-deleted"
switch (response.name) {
// existing branches …
// no branch for "sandbox-drive-snapshot-created"
// no branch for "sandbox-drive-snapshot-deleted"
default:
throw new Error("unexpected name");
}
can now return "organization-dsync-delete"
switch (response.type) {
// existing branches …
// no branch for "organization-dsync-delete"
default:
throw new Error("unexpected type");
}