> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from API V2

> Move an integration off the deprecated v2 API. Find the replacement for each v2 route and the behavior that changed.

Whop has deprecated API v2 (`https://api.whop.com/api/v2` and `https://api.whop.com/v2`). This page maps each v2 route to its replacement in the Current API at `https://api.whop.com/api/v1` and lists what behaves differently.

## Find your v2 calls

Every v2 response carries two headers:

| Header | Example | Meaning |
| - | - | - |
| `Deprecation` | `@1791417600` | Whop deprecated this endpoint. The value is the deprecation date as Unix seconds. |
| `Link` | `<https://api.whop.com/api/v1/memberships/mem_xxxxxxxxxxxxx>; rel="successor-version", <https://docs.whop.com/developer/api/migrate-from-v2>; rel="deprecation"` | The `successor-version` URL is the Current API request that replaces this one. The `deprecation` URL is this page. |

Log responses that include `Deprecation` to find every v2 call your integration still makes.

## Switch to the Current API

<Steps>
  <Step title="Keep your API key">
    The Current API uses the same account API keys. If a request returns `403`, the key is missing the permission that endpoint needs. Add it in **Developer → API keys**.
  </Step>

  <Step title="Install the SDK and pin a version">
    The official SDKs call the Current API and send the API version they were built against.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { WhopClient } from "@whop/sdk";

      const client = new WhopClient({ token: process.env.WHOP_API_KEY });

      const membership = await client.memberships.retrieve({ id: "mem_xxxxxxxxxxxxx" });
      ```

      ```python Python theme={null}
      import os

      from whop_sdk import Whop

      client = Whop(token=os.environ["WHOP_API_KEY"])

      membership = client.memberships.retrieve("mem_xxxxxxxxxxxxx")
      ```

      ```bash cURL theme={null}
      curl https://api.whop.com/api/v1/memberships/mem_xxxxxxxxxxxxx \
        -H "Authorization: Bearer $WHOP_API_KEY" \
        -H "Api-Version-Date: 2026-08-21-1"
      ```
    </CodeGroup>

    If you call the API without an SDK, send `Api-Version-Date` on every request. [API versions](/developer/api/versioning) explains pinning.
  </Step>

  <Step title="Replace each route">
    Use the tables below, then remove the v2 base URL from your code.
  </Step>
</Steps>

## Route map

### Memberships and licenses

| v2 route | Current API | Notes |
| - | - | - |
| `GET /memberships` | `GET /memberships` | Cursor pagination. See [Pagination](#pagination). |
| `GET /memberships/{id}` | `GET /memberships/{id}` | Also accepts a license key in place of the `mem_` ID. |
| `POST /memberships/{id}` | `PATCH /memberships/{id}` | Send `metadata` to merge keys. Send `{}` to clear it. |
| `POST /memberships/{id}/cancel` | `POST /memberships/{id}/cancel` | Pass `cancel_at_period_end: true` to keep access until the renewal date. |
| `POST /memberships/{id}/terminate` | `POST /memberships/{id}/cancel` | Omit `cancel_at_period_end` to revoke access immediately. |
| `POST /memberships/{id}/add_free_days` | `POST /memberships/{id}/extend` | |
| `POST /memberships/{id}/validate_license` | `GET /memberships/{id}` and `PATCH /memberships/{id}` | Check the key and bind a machine from your server. See [License keys](#license-keys). |
| `GET /licenses` | `GET /memberships` | Each membership has a `license_key` when its product includes the Software app. |

### Members and customers

| v2 route | Current API | Notes |
| - | - | - |
| `GET /members` | `GET /members` | |
| `GET /customers` | `GET /members` | Customers and members are the same resource. |
| `GET /members/{search}` | `GET /members` | Filter with `user_ids` or `query`. `GET /members/{id}` takes only a `mber_` ID. |
| `GET /customers/{search}` | `GET /members` | Same as above. |

### Payments and checkout

| v2 route | Current API | Notes |
| - | - | - |
| `GET /payments` | `GET /payments` | Filters are singular, such as `status` and `membership_id`. |
| `GET /payments/{id}` | `GET /payments/{id}` | |
| `POST /payments/{id}/refund` | `POST /payments/{id}/refund` | |
| `GET /payment_fees?payment_id=` | `GET /payments/{id}/fees` | |
| `GET /payment_methods` | `GET /payment_methods` | |
| `POST /checkout_requests` | `POST /payments` | Pass `member_id` (`mber_`) and `payment_method_id`. The response is the payment. |
| `GET /checkout_requests/{id}` | `GET /payments/{id}` | `POST /payments` returns the payment directly, so you don't need to poll. |
| `GET /checkout_sessions` | `GET /checkout_configurations` | Checkout sessions are now called checkout configurations. |
| `GET /checkout_sessions/{id}` | `GET /checkout_configurations/{id}` | |
| `POST /checkout_sessions` | `POST /checkout_configurations` | The response includes `purchase_url`. |
| `DELETE /checkout_sessions/{id}` | `DELETE /checkout_configurations/{id}` | |

### Products and plans

Plans are now called variants. Variant IDs keep the `plan_` prefix, so stored IDs still work.

| v2 route | Current API | Notes |
| - | - | - |
| `GET /products`, `GET /access_passes` | `GET /products` | Access passes are now called products. |
| `GET /products/{id}`, `GET /access_passes/{id}` | `GET /products/{id}` | |
| `POST /products/{id}`, `POST /access_passes/{id}` | `PATCH /products/{id}` | |
| `GET /plans` | `GET /variants` | |
| `GET /plans/{id}` | `GET /variants/{id}` | |
| `POST /plans` | `POST /variants` | |
| `POST /plans/{id}` | `PATCH /variants/{id}` | |
| `POST /plans/{id}/create_quick_link` | `POST /variants` | Create a variant with `visibility: "quick_link"`, `stock`, and `short_link`. The Current API doesn't copy an existing plan, so send its price fields too. |
| `GET /promo_codes` | `GET /promo_codes` | |
| `GET /promo_codes/{id}` | `GET /promo_codes/{id}` | |
| `POST /promo_codes` | `POST /promo_codes` | |
| `POST /promo_codes/{id}` | `POST /promo_codes/{id}/activate`, `POST /promo_codes/{id}/deactivate` | Covers `status` only. Create a new promo code to change its variants, stock, or metadata. Then deactivate the old one. |
| `DELETE /promo_codes/{id}` | `DELETE /promo_codes/{id}` | |

### Account, experiences, reviews, and webhooks

| v2 route | Current API | Notes |
| - | - | - |
| `GET /company` | `GET /accounts/me` | |
| `GET /experiences` | `GET /experiences` | |
| `GET /experiences/{id}` | `GET /experiences/{id}` | |
| `POST /experiences/{id}` | `PATCH /experiences/{id}` | |
| `DELETE /experiences/{id}` | `DELETE /experiences/{id}` | |
| `GET /reviews` | `GET /reviews` | |
| `GET /reviews/{id}` | `GET /reviews/{id}` | |
| `GET /webhooks` | `GET /webhooks` | |
| `GET /webhooks/{id}` | `GET /webhooks/{id}` | |
| `POST /webhooks` | `POST /webhooks` | See [Webhooks](#webhooks). |
| `POST /webhooks/{id}` | `PATCH /webhooks/{id}` | |
| `DELETE /webhooks/{id}` | `DELETE /webhooks/{id}` | |

## What changed

### Partial updates

v2 updated a resource with `POST /resource/{id}`. The Current API uses `PATCH /resource/{id}` and changes only the fields you send.

### Pagination

v2 paged with `page` and `per` and returned `pagination.current_page` and `pagination.total_page`. Current API lists return a `data` array and a `page_info` object. Pass `first` to set the page size and `after` with the previous `page_info.end_cursor` to get the next page. Stop when `page_info.has_next_page` is `false`. The SDKs page for you.

<CodeGroup>
  ```typescript TypeScript theme={null}
  for await (const membership of client.memberships.list({ product_id: "prod_xxxxxxxxxxxxx" })) {
    console.log(membership.id);
  }
  ```

  ```python Python theme={null}
  for membership in client.memberships.list(product_id="prod_xxxxxxxxxxxxx"):
      print(membership.id)
  ```
</CodeGroup>

### License keys

v2 `validate_license` checked a key and bound it to a machine in one call. In the Current API, your server retrieves the membership by license key, checks its `status`, and writes a hardware ID to `metadata` the first time someone uses the key. The [SaaS guide](/supported-business-models/saas#license-key-integration) has the full example.

Keep these calls on your server. Your account API key can change every membership in your business, so never ship it inside downloadable software.

### Webhooks

Webhooks created through v2 keep sending v2-format payloads. To receive the current format, create a new webhook from the dashboard or with `POST /webhooks`, move your handler to it, then delete the v2 webhook. [Webhooks](/developer/guides/webhooks) covers signature checks and event types.

## Next steps

<CardGroup cols={2}>
  <Card title="API versions" icon="calendar" href="/developer/api/versioning">
    Pin a version so later changes never break your integration.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/developer/guides/webhooks">
    Verify signatures and handle the current event format.
  </Card>

  <Card title="Manage memberships" icon="id-card" href="/developer/guides/memberships">
    Cancel, pause, extend, and update memberships.
  </Card>

  <Card title="SaaS" icon="laptop" href="/supported-business-models/saas">
    Gate a web app or downloadable software with the Current API.
  </Card>
</CardGroup>


## Related topics

- [API Versions](/developer/api/versioning.md)
- [Social Account (Deprecated)](/api-reference/beta/social-accounts/social-account.md)
- [Accept Payments](/developer/guides/accept-payments.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.