Skip to content
Docs

Using the Integrations REST API

Learn how to authenticate and use the Integrations REST API to build your native integration with Vercel.

When a customer uses your integration, the following two APIs are used for interaction and communication between the user, Vercel and the provider integration:

  • Vercel calls the provider API: You implement the Vercel Marketplace Partner API endpoints on your integration server. Vercel calls them to manage resources, handle installations, and process billing.
  • The provider calls the Vercel API: Vercel provides these endpoints. You call them from your integration server to interact with Vercel's platform.

When building your integration, you'll implement the partner endpoints and call the Vercel endpoints as needed.

See the Native Integration Flows to understand how these endpoints work together.

The authentication method depends on whether Vercel is calling the integration provider's API or the provider is calling Vercel's API.

There are two authentication methods available:

  • User authentication: The user initiates the action. You receive a JWT token that identifies the user making the request.
  • System authentication: Your integration performs the action automatically. You use account-level OpenID Connect (OIDC) credentials to authenticate.

System authentication uses OIDC tokens that represent your integration account, not a specific user. This lets you make API calls to Vercel without requiring user interaction.

  • Automatic balance top-ups for prepayment plans
  • Background synchronization tasks
  • Automated resource management
  • Any operation that should run without user action
  • Installation cleanup operations when the Vercel account is deleted
  • User-initiated actions
  • Operations that require user consent
  • Actions tied to a specific user's context
  • Verify the OIDC token signature and claims: Always validate the token signature using Vercel's OIDC configuration. Check the aud claim matches your integration ID, and the sub claim identifies the authenticated user or account.
  • For user authentication always validate the user's role.

Review the user authentication and system authentication specifications to help you implement each method.

When your integration calls Vercel's API, you authenticate using an access token. You receive this token during the installation process when you call the Upsert Installation API. The response includes a credentials object with an access_token that you use as a bearer token for subsequent API calls.

You can also use OAuth2 to obtain access tokens for user-specific operations.

Vercel generally ensures that email addresses included in Marketplace tokens and member information are verified. You usually don't need an additional email verification flow.

Vercel verifies user email addresses through one of the following methods:

  • Direct verification by Vercel: When users sign up or sign in with an email, Vercel sends a verification email. Users must click the verification link before they can use the platform.
  • Trusted social identity providers: When users authenticate through social login with a trusted identity provider for the email domain, Vercel accepts the email as verified. For example, Vercel trusts Google for gmail.com addresses and Apple for icloud.com addresses.
  • Enterprise Managed Users (EMU): When an Enterprise team verifies a domain and a user authenticates through the team's SAML identity provider with an email on that domain, Vercel accepts the email as verified. Directory Sync can provision and manage the user, but verification comes from the verified domain and SAML-authenticated identity.

Marketplace tokens also include either a user_email_verified or email_verified claim with Vercel's most up-to-date verification status. If your integration needs to adjust its behavior based on this status, check the applicable claim instead of assuming that the email remains verified.

This guidance applies to:

Vercel initiates SSO as part of the Open in Provider flow.

  1. Vercel sends the user to the provider redirectLoginUrl, with the OAuth authorization code and other parameters
  2. The provider calls the SSO Token Exchange, which validates the SSO request and returns OIDC and access tokens
  3. The user gains authenticated access to the requested resource.

Parameters:

The SSO request to the redirectLoginUrl will include the following authentication parameters:

  • mode. The mode of the OAuth authorization is always set to sso.
  • code: The OAuth authorization code.
  • state: The state parameter that was passed in the OAuth authorization request.

The code and state parameters will be passed back to Vercel in the SSO Token Exchange request.

Additionally, the SSO request to the redirectLoginUrl may include the following optional context parameters:

  • product_id: The ID of the provider's product
  • resource_id: The ID of the provider's resource
  • check_id: The ID of the deployment check, when the resource is associated with a deployment check. Example: "chk_abc123".
  • project_id: The ID of the Vercel project, for instance, when the resource is connected to the Vercel project. Example: "prj_ff7777b9".
  • experimentation_item_id: See Experimentation flow.
  • invoice_id: The ID of the provider's invoice
  • pr: The URL of the pull request in the Vercel project, when known in the context. Example: https://github.com/owner1/repo1/pull/123.
  • path: Indicates the area where the user should be redirected to after SSO. The possible values are: "billing", "usage", "onboarding", "secrets", and "support".
  • url: The provider-specific URL to redirect the user to after SSO. Must be validated by the provider for validity. The data fields that are allowed to provide sso: URLs, such as Notification.href, will automatically propagate the provided URL in this parameter.

The provider should match the most appropriate part of their dashboard to the user's context.

Using SSO with API responses:

You can trigger SSO by using sso: URLs in your API responses. When users click these links, Vercel initiates the SSO flow before redirecting them to your platform. The sso: prefix works in any URL field that supports it, such as installation notification links or resource URLs.

Format:

sso:https://your-integration.com/resource-page

When a user clicks a link with an sso: URL:

  1. Vercel initiates the SSO flow
  2. Your provider validates the SSO request via the SSO Token Exchange
  3. The user is redirected to the target URL with authenticated access

Example with notifications:

upsert-installation-with-sso.ts
// When creating or updating an installation, include an sso: URL
const response = await fetch(
  `https://api.vercel.com/v1/installations/${installationId}`,
  {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${vercelToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      notification: {
        title: 'Review your usage',
        message: 'Your monthly usage report is ready',
        href: 'sso:https://your-integration.com/dashboard/usage',
        type: 'info',
      },
    }),
  },
);

The integration provider can initiate the SSO process from their side. This helps to streamline the authentication process for users coming from the provider's platform and provides security when a user attempts to access a resource managed by a Vercel Marketplace integration.

To initiate SSO, an integration provider needs to construct a URL using the following format:

https://vercel.com/sso/integrations/{URLSlug}/{installationId}?{query}
  • URLSlug: The unique identifier for your integration in the Vercel Integrations Marketplace
  • installationId: The ID of the specific installation for the user
  • query: Optional query parameters to include additional information

Example:

Let's say you have an AWS integration with the following details:

  • URLSlug: aws-marketplace-integration-demo
  • installationId: icfg_PSFtkFqr5djKRtOkNtOHIfSd
  • Additional parameter: resource_id=123456

The constructed URL would look like this:

https://vercel.com/sso/integrations/aws-marketplace-integration-demo/icfg_PSFtkFqr5djKRtOkNtOHIfSd?resource_id=123456

Flow:

  1. The provider constructs and redirects the user to the SSO URL
  2. Vercel validates the SSO request and confirms user access
  3. After successfully validating the request, Vercel redirects the user back to the provider using the same flow described in the Vercel Initiated SSO
  4. The user gains authenticated access to the requested resource

Deep links let your integration send a user straight to the relevant page for an installation in the Vercel dashboard. Use them in your own UI, emails, or notifications to bring users back to Vercel with a single URL.

You don't need to know the user's team to build a deep link. Vercel resolves the team from the installation, confirms the signed-in user has access, and then redirects to the destination. If the user isn't signed in, Vercel prompts them to sign in first.

All deep links share the same base:

https://vercel.com/d/dashboard/integrations/{URLSlug}/{installationId}{path}
  • URLSlug: The unique identifier for your integration in the Vercel Integrations Marketplace
  • installationId: The ID of the specific installation for the user
  • path: The optional path segment that selects the destination, as described below

Vercel supports the following deep links:

DestinationURLDescription
Installation overviewhttps://vercel.com/d/dashboard/integrations/{URLSlug}/{installationId}The installation's page in the user's team, where they manage settings, usage, and invoices.
Installation projectshttps://vercel.com/d/dashboard/integrations/{URLSlug}/{installationId}/projectsThe projects connected to the installation.
Installation settingshttps://vercel.com/d/dashboard/integrations/{URLSlug}/{installationId}/settingsThe installation's settings page.
Resourcehttps://vercel.com/d/dashboard/integrations/{URLSlug}/{installationId}/resources/{resourceId}A specific resource that belongs to the installation. resourceId accepts either Vercel's resource ID or the external ID you assigned to it.

Say you have an AWS integration with the following details:

  • URLSlug: aws-marketplace-integration-demo
  • installationId: icfg_PSFtkFqr5djKRtOkNtOHIfSd

To send the user to the installation's projects page, construct this URL:

https://vercel.com/d/dashboard/integrations/aws-marketplace-integration-demo/icfg_PSFtkFqr5djKRtOkNtOHIfSd/projects

Vercel resolves the team, verifies access, and redirects the user to the projects page for that installation.

Get details about team members who have access to an installation. Use this endpoint to retrieve member information for access control, audit logs, or displaying member details in your integration.

To retrieve information about a specific team member associated with an installation, use the /v1/installations/{installationId}/member/{memberId} endpoint.

  • installationId - The installation ID
  • memberId - The member ID
get-member-info.ts
async function getMemberInfo(
  installationId: string,
  memberId: string
): Promise<MemberInfo> {
  const response = await fetch(
    `https://api.vercel.com/v1/installations/${installationId}/member/${memberId}`,
    {
      headers: {
        'Authorization': `Bearer ${token}`,
      },
    }
  );
 
  if (!response.ok) {
    throw new Error(`Failed to get member info: ${response.statusText}`);
  }
 
  return response.json();
}
get-member-info-response.json
{
  "id": "member_abc123",
  "name": "Timmy Triangle",
  "email": "timmy@example.com",
  "role": "ADMIN",
  "avatar": "https://example.com/avatar.jpg",
  "createdAt": "2025-01-15T10:00:00Z"
}

Members can have the following roles:

  • ADMIN - Full access to the installation and its resources
  • USER - Limited access, can use resources but can't modify settings

Check the member's role to determine what actions they can perform below.

Installation notifications appear in the Vercel dashboard to alert users about important information or actions needed for their installation. You can set notifications when creating or updating installations.

Update the notification field using the PATCH /v1/installations/{installationId} endpoint as shown below:

update-installation-notification.ts
interface Notification {
  title: string;
  message: string;
  href?: string;
  type?: 'info' | 'warning' | 'error';
}
 
async function updateInstallationNotification(
  installationId: string,
  notification: Notification
) {
  const response = await fetch(
    `https://api.vercel.com/v1/installations/${installationId}`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ notification }),
    }
  );
 
  if (!response.ok) {
    throw new Error(`Failed to update notification: ${response.statusText}`);
  }
 
  return response.json();
}
 
// Example usage with regular URL:
await updateInstallationNotification('icfg_abc123', {
  title: 'Action Required',
  message: 'Please complete your account setup',
  href: 'https://your-integration.com/setup',
  type: 'warning',
});
 
// Or with SSO-enabled URL for authenticated access:
// href: 'sso:https://your-integration.com/setup',
update-installation-notification-response.json
{
  "id": "icfg_abc123",
  "notification": {
    "title": "Action Required",
    "message": "Please complete your account setup",
    "href": "https://your-integration.com/setup",
    "type": "warning"
  }
}

Use different notification types to indicate severity:

  • info - Informational message (default)
  • warning - Warning that requires attention
  • error - Error that needs immediate action

The notification href field supports special sso: URLs that trigger Single Sign-On before redirecting users to your destination. This ensures users are authenticated before accessing resources on your platform.

Format:

sso:https://your-integration.com/resource-page

When a user clicks a notification link with an sso: URL:

  1. Vercel initiates the SSO flow (as described in Vercel initiated SSO)
  2. Your provider validates the SSO request via the SSO Token Exchange
  3. The user is redirected to the target URL with authenticated access

Example:

notification-with-sso.ts
await updateInstallationNotification('icfg_abc123', {
  title: 'Review your usage',
  message: 'Your monthly usage report is ready',
  href: 'sso:https://your-integration.com/dashboard/usage',
  type: 'info',
});

Use sso: URLs in notification links when they point to resources that require authentication on your platform. For public pages or general information, use regular HTTPS URLs.

Remove a notification by setting it to null:

clear-installation-notification.ts
async function clearInstallationNotification(installationId: string) {
  const response = await fetch(
    `https://api.vercel.com/v1/installations/${installationId}`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ notification: null }),
    }
  );
 
  return response.json();
}

You can find the value of the notification field by calling the /v1/installations/{installationId} endpoint as shown below:

get-installation-with-notification.ts
async function getInstallation(installationId: string) {
  const response = await fetch(
    `https://api.vercel.com/v1/installations/${installationId}`,
    {
      headers: {
        'Authorization': `Bearer ${token}`,
      },
    }
  );
 
  const installation = await response.json();
 
  if (installation.notification) {
    console.log(`Notification: ${installation.notification.title}`);
    console.log(`Message: ${installation.notification.message}`);
  }
 
  return installation;
}

Customers can use the Vercel dashboard, CLI, or REST API to connect a Native Marketplace resource to Production, Preview, Development, and individual Custom Environments. Vercel stores the selected Custom Environments on the project’s resource connection.

To supply a Preview secret value for Custom Environments, set secrets[].environmentOverrides.preview to a non-empty value. If the Preview override is omitted, Vercel uses the default secrets[].value.

Vercel maps each Custom Environment to preview in provider-facing project connection events and deployment action targets. Vercel removes duplicate target values. These target fields do not include Custom Environment slugs or IDs.

The REST API resource connection request accepts production, preview, development, or a Custom Environment slug or ID from the target project in envVarEnvironments. Vercel validates each Custom Environment against the target project. If the request uses a stable ID, Vercel stores the matching slug in the connection record. If the request omits envVarEnvironments, Vercel uses all three built-in environments. The default does not include Custom Environments.

When you provision a resource or update secrets, you can include an optional prefix field for each secret. Vercel prepends this prefix to the secret name when creating environment variables in connected projects. This lets users connect the same resource type to multiple projects, or multiple resources to one project, without name collisions.

For example, to return secrets with a prefix during provisioning:

Provision resource response with prefixes
{
  "secrets": [
    {
      "name": "PGHOST",
      "value": "db.example.com",
      "prefix": "ACME"
    },
    {
      "name": "PGPASSWORD",
      "value": "your_password_here",
      "prefix": "ACME"
    }
  ]
}

This creates ACME_PGHOST and ACME_PGPASSWORD as environment variables in the connected project.

Vercel normalizes hyphens and spaces in prefix values to underscores. For example, a prefix of MY-DB becomes MY_DB. If a secret name already starts with the prefix (for example, ACME_PGHOST with prefix ACME), Vercel skips prefixing to avoid duplication.

Users can also set a custom prefix when connecting a resource to a project. Learn more about how prefixes work.

When your integration provisions resources with credentials, you should implement secrets rotation to allow users to update credentials securely. Learn how to implement secrets rotation in your integration.

Vercel can send integration-resource.project-connected and integration-resource.project-disconnected events after a resource connection changes. These webhooks describe provider-facing targets, not every environment scope change. If only the Custom Environment scope changes and the provider-facing target set remains unchanged, Vercel does not send either webhook event. For example, replacing qa with staging keeps the provider-facing target as preview and does not produce either event.

When Vercel sends either event, the payload.targets field contains a deduplicated list of production, preview, and development targets. Vercel maps each Custom Environment to preview. The field does not include the exact Custom Environment slug or ID. For example, qa produces ['preview']. A selection of production, preview, and qa produces ['production', 'preview'].

See the Webhooks API reference for all project connection payload fields.

You can receive billing events with webhooks to stay informed about invoice status changes and take appropriate actions. For an overview of the billing process and how to submit invoices, see Billing and Refunds.

You can receive the following events:

You should verify webhook signatures to ensure requests come from Vercel. Integration webhooks use your Integration Secret (also called Client Secret) from the Integration Console for signature verification. Follow the Securing webhooks section of the Webhooks API Reference to learn more.

You can implement handlers for each billing event type to manage invoice lifecycle and resource access.

When an invoice is created, you can prepare your systems for billing or send notifications.

Event: marketplace.invoice.created

handle-invoice-created.ts
async function handleInvoiceCreated(webhook: WebhookPayload) {
  const {
    configuration,
    invoiceId,
    invoiceTotal,
    installationId,
    invoiceDate,
  } = webhook.payload;
 
  // Log invoice creation
  console.log(
    `Invoice ${invoiceId} created for installation ${installationId}`,
  );
 
  // Update your internal records
  await updateInvoiceRecord(invoiceId, {
    status: 'created',
    amount: invoiceTotal,
    createdAt: invoiceDate,
  });
 
  // Optional: Send notification to customer
  await sendInvoiceNotification(configuration.id, invoiceId);
}
invoice-created-webhook-payload.json
{
  "id": "evt_abc123",
  "type": "marketplace.invoice.created",
  "payload": {
    "configuration": {
      "id": "icfg_def456"
    },
    "installationId": "oac_ghi789",
    "invoiceId": "inv_xyz987",
    "externalInvoiceId": "1234-5678",
    "period": {
      "start": "2025-01-15T10:00:00Z",
      "end": "2025-02-15T10:00:00Z"
    },
    "invoiceDate": "2025-02-15T10:00:00Z",
    "invoiceTotal": "29.99"
  }
}

When an invoice is paid, activate resources or update billing status.

Event: marketplace.invoice.paid

handle-invoice-paid.ts
async function handleInvoicePaid(webhook: WebhookPayload) {
  const { configuration, invoiceId } = webhook.payload;
 
  console.log(`Invoice ${invoiceId} paid`);
 
  // Update invoice status
  await updateInvoiceRecord(invoiceId, {
    status: 'paid',
    paidAt: Date.now(),
  });
 
  // Activate resources if they were suspended
  const resources = await getResourcesForInstallation(configuration.id);
  for (const resource of resources) {
    if (resource.status === 'suspended') {
      await activateResource(resource.id);
    }
  }
 
  // Update billing plan if needed
  await updateBillingPlan(configuration.id);
}
invoice-paid-webhook-payload.json
{
  "id": "evt_def456",
  "type": "marketplace.invoice.paid",
  "payload": {
    "configuration": {
      "id": "icfg_def456"
    },
    "installationId": "oac_ghi789",
    "invoiceId": "inv_xyz987",
    "externalInvoiceId": "1234-5678",
    "period": {
      "start": "2025-01-15T10:00:00Z",
      "end": "2025-02-15T10:00:00Z"
    },
    "invoiceDate": "2025-02-15T10:00:00Z",
    "invoiceTotal": "29.99"
  }
}

Fires when an invoice payment fails.

Event: marketplace.invoice.notpaid

The current webhook payload doesn't include retry attempt information. You'll need to track retry attempts in your system or query the invoice status directly

handle-invoice-not-paid.ts
async function handleInvoiceNotPaid(webhook: WebhookPayload) {
  const { configuration, invoiceId } = webhook.payload;
 
  console.log(`Invoice ${invoiceId} not paid`);
 
  // Request fresh invoice
  const invoiceDetails = await getInvoiceDetails(invoiceId);
 
  if (invoiceDetails.state === 'notpaid') {
    // Update invoice status
    await updateInvoiceRecord(invoiceId, {
      status: 'not_paid',
      notPaidAt: Date.now(),
    });
 
    // Check if this is the final attempt (you may need to query invoice status)
    // Prefer `marketplace.invoice.overdue`
    const isFinalAttempt = checkAttempts(invoiceId);
    if (isFinalAttempt) {
      // Suspend resources after final payment failure
      const resources = await getResourcesForInstallation(configuration.id);
      for (const resource of resources) {
        await suspendResource(resource.id, {
          reason: 'payment_failed',
          invoiceId,
        });
      }
 
      // Notify customer
      await sendPaymentFailureNotification(configuration.id, invoiceId);
    } else {
      // Schedule retry or send reminder
      await schedulePaymentRetry(invoiceId);
    }
  }
}
invoice-not-paid-webhook-payload.json
{
  "id": "evt_ghi789",
  "type": "marketplace.invoice.notpaid",
  "payload": {
    "configuration": {
      "id": "icfg_def456"
    },
    "installationId": "oac_ghi789",
    "invoiceId": "inv_xyz987",
    "externalInvoiceId": "1234-5678",
    "period": {
      "start": "2025-01-15T10:00:00Z",
      "end": "2025-02-15T10:00:00Z"
    },
    "invoiceDate": "2025-02-15T10:00:00Z",
    "invoiceTotal": "29.99"
  }
}

When an invoice isn't paid after the grace period, suspend resources or take other actions.

Event: marketplace.invoice.overdue

handle-invoice-overdue.ts
async function handleInvoiceOverdue(webhook: WebhookPayload) {
  const { configuration, invoiceId } = webhook.payload;
 
  console.log(`Invoice ${invoiceId} overdue`);
 
  // Request the invoice from the API, in case event is stale
  const invoiceDetails = await getInvoiceDetails(invoiceId);
 
  if (invoiceDetails.state === 'overdue') {
    // Update invoice status
    await updateInvoiceRecord(invoiceId, {
      status: 'overdue',
      notPaidAt: new Date(),
    });
 
    // Suspend resources after final payment failure
    const resources = await getResourcesForInstallation(configuration.id);
    for (const resource of resources) {
      await suspendResource(resource.id, {
        reason: 'payment_overdue',
        invoiceId,
      });
    }
 
    // Notify customer
    await sendPaymentFailureNotification(configuration.id, invoiceId);
  }
}
invoice-overdue-webhook-payload.json
{
  "id": "evt_ghi789",
  "type": "marketplace.invoice.overdue",
  "payload": {
    "configuration": {
      "id": "icfg_def456"
    },
    "installationId": "oac_ghi789",
    "invoiceId": "inv_xyz987",
    "externalInvoiceId": "1234-5678",
    "period": {
      "start": "2025-01-15T10:00:00Z",
      "end": "2025-02-15T10:00:00Z"
    },
    "invoiceDate": "2025-02-15T10:00:00Z",
    "invoiceTotal": "29.99"
  }
}

When an invoice is refunded, update records and handle resource access accordingly.

Event: marketplace.invoice.refunded

handle-invoice-refunded.ts
async function handleInvoiceRefunded(webhook: WebhookPayload) {
  const { configuration, invoiceId, amount } = webhook.payload;
 
  console.log(`Invoice ${invoiceId} refunded`);
 
  // Update invoice status
  await updateInvoiceRecord(invoiceId, {
    status: 'refunded',
    refundedAt: Date.now(),
    refundAmount: amount,
  });
 
  // Adjust billing records
  await adjustBillingRecords(configuration.id, {
    type: 'refund',
    amount: amount,
    invoiceId,
  });
 
  // Optional: Notify customer
  await sendRefundNotification(configuration.id, invoiceId);
}
invoice-refunded-webhook-payload.json
{
  "id": "evt_jkl012",
  "type": "marketplace.invoice.refunded",
  "payload": {
    "configuration": {
      "id": "icfg_def456"
    },
    "installationId": "oac_ghi789",
    "invoiceId": "inv_xyz987",
    "externalInvoiceId": "1234-5678",
    "period": {
      "start": "2025-01-15T10:00:00Z",
      "end": "2025-02-15T10:00:00Z"
    },
    "amount": "15.00",
    "reason": "Refund"
  }
}
Last updated September 17, 2026

Was this helpful?

supported.