Plaid logo
Docs
ALL DOCS

API

  • Overview
  • Libraries
  • API versioning
  • Postman Collection
  • Webhooks
Payments and Funding
  • Auth
  • Signal and Balance
  • Identity
  • Transfer
  • Investments Move
  • Payment Initiation (Europe)
  • Virtual Accounts
Financial Insights
  • Transactions
  • Investments
  • Liabilities
  • Enrich
KYC/AML and anti-fraud
  • Look up Dashboard users
  • Identity Verification
  • Monitor
Credit and Underwriting
  • Consumer Report (by Plaid Check)
  • Assets
  • Statements
  • Income
Instant Onboarding
  • Plaid Layer
Fundamentals
  • Items
  • Accounts
  • Institutions
  • Sandbox
  • Link
  • Users
  • Consent
  • Network
  • OAuth
Partnerships
  • Processor tokens
  • Processor partners
  • Reseller partners
Plaid logo
Docs
Plaid.com
Log in
Get API Keys
Open nav
Close search modal
Ask Bill!
Ask Bill!
Hi! I'm Bill! You can ask me all about the Plaid API. Try asking questions like:
    Pssst -- I also moonlight as your IDE's research librarian! Plug me in via the Plaid MCP Server.
    Note: Bill isn't perfect. He's just a robot platypus that reads our docs for fun. You should treat his answers with the same healthy skepticism you might treat any other answer on the internet. This chat may be logged for quality and training purposes. Please don't send Bill any PII -- he's scared of intimacy. All chats with Bill are subject to Plaid's Privacy Policy.

    Migrate to new User APIs

    Migration guide for existing Consumer Report and Income integrations on legacy User APIs

    This guide is for customers who integrated with Consumer Report by Plaid Check (CRA) before December 10, 2025, or with Plaid Income Verification before August 17, 2026. It explains how to migrate from the legacy User APIs and product endpoints, which use user_token as the primary identifier, to the new User APIs and user_id-based product endpoints. Migration is optional — your existing integration will continue to work, and there is currently no deadline to migrate.

    This guide covers both Plaid Check Consumer Report and Plaid Income Verification. Product-specific steps are labeled below.

    Are you on the legacy API?

    Plaid Check clients who began using /user/create on or after December 10, 2025, and Income clients who began using it on or after August 17, 2026, are on the new User API by default and do not need to migrate. If you aren't sure, call /user/create without with_upgraded_user: true. If the response does not include a user_token, your client is on the new API by default. If the response includes a user_token, then you are eligible to migrate, if you haven't done so already.

    Overview

    Plaid's new User APIs introduce a unified user identifier, user_id, that is consistent across all Plaid user-based products. For existing CRA and Income customers, the migration has two parts depending on whether a user already exists in your system:

    Users you have already created have a stored user_token (format: user-production-*). After migration, pass that user_token value in the user_id field of all applicable CRA or Income API requests and webhook correlation. No changes are needed to the users themselves.

    New users created after migration use the updated /user/create schema, which returns a user_id (prefixed with usr_) instead of a user_token. Use that user_id in all subsequent API calls.

    The old /user/create response returned both a user_token and a legacy user_id (an unprefixed string, distinct from the usr_*-prefixed user_id used in the new APIs). After migration, set the value of the user_id field in API requests to your stored user_token — not the legacy user_id.

    What's changing

    To migrate, you will need to:

    • Replace user_token with user_id in all applicable CRA or Income API calls, /link/token/create, and webhook correlation. For existing users, set the value of the user_id field to your stored user_token value.
    • Update /user/create to include with_upgraded_user: true. CRA customers must also replace consumer_report_user_identity with the new identity schema. /user/create is now idempotent: if you call it with a client_user_id that already exists, it returns the existing user_id rather than an error.
    • Update webhook handling for the products you use:
      • For CRA, listen for USER_CHECK_REPORT_READY and USER_CHECK_REPORT_FAILED instead of CHECK_REPORT_READY and CHECK_REPORT_FAILED.
      • For Income, listen for USER_INCOME_VERIFICATION instead of INCOME_VERIFICATION, USER_INCOME_VERIFICATION_RISK_SIGNALS instead of INCOME_VERIFICATION_RISK_SIGNALS, and USER_INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED instead of INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED.
      • Cash Flow Updates webhooks are consolidated into a single CASH_FLOW_INSIGHTS_UPDATED event.
    • Use the new /user/get endpoint to retrieve identity details about any user, including those created via the legacy User API.

    For full details, see New User API overview and the migration steps below.

    Migration steps

    Update your Plaid client library SDK

    Before making any API changes, upgrade your Plaid client library SDK to the minimum version listed below. Older versions do not support the new request schemas and will return errors.

    Minimum required versions:

    • Node.js: 41.0.0
    • Python: 38.0.0
    • Go: 41.0.0
    • Java: 39.0.0
    • Ruby: 45.0.0
    Handle existing users

    For users you created via the legacy /user/create, you have a stored user_token (format: user-production-*). When making API calls for them:

    • Pass your stored user_token value in the user_id field for all applicable CRA or Income API requests and webhook correlation.
    • Do not use the legacy user_id from the old /user/create response — that value is not used for CRA or Income integrations after migration.
    Create new users with the new User API

    For new users, call /user/create with the following changes:

    • Include with_upgraded_user: true in the request body.
    • Replace consumer_report_user_identity with an identity object containing name, emails, addresses, phone_numbers, date_of_birth, and optionally id_numbers (last 4 SSN digits).
    • The response returns a single user_id — there is no user_token. Store this user_id as the identifier for all subsequent API calls and webhooks.
    /user/create with new User API schema for CRA
    curl -X POST https://sandbox.plaid.com/user/create \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "client_user_id": "c0e2c4ee-b763-4af5-cfe9-46a46bce883d",
        "with_upgraded_user": true,
        "identity": {
          "name": {
            "given_name": "Carmen",
            "family_name": "Berzatto"
          },
          "date_of_birth": "1987-01-31",
          "emails": [
            { "data": "carmen@example.com", "primary": true }
          ],
          "phone_numbers": [
            { "data": "+13125551212", "primary": true }
          ],
          "addresses": [
            {
              "street_1": "3200 W Armitage Ave",
              "city": "Chicago",
              "region": "IL",
              "country": "US",
              "postal_code": "60657",
              "primary": true
            }
          ],
          "id_numbers": [
            { "value": "1234", "type": "us_ssn_last_4" }
          ]
        }
      }'
    /user/create with new User API schema for Income
    curl -X POST https://sandbox.plaid.com/user/create \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "client_user_id": "c0e2c4ee-b763-4af5-cfe9-46a46bce883d",
        "with_upgraded_user": true
      }'

    /user/create is now idempotent. If you call it with a client_user_id that already exists, it returns the existing user_id with a 200 status rather than an error; if you include an identity object in that call, it will be attached to the existing user. If the client_user_id is new, a new user_id is created and returned with a 201 status.

    Additionally, in the old flow a client_user_id could never be reused to create a new user, even after calling /user/remove. In the new flow, once /user/remove has been called on a user_id, you can call /user/create again with the same client_user_id to create a new user.

    When calling the new /user/create with a client_user_id that was previously created via the legacy API, the response user_id may be in user-production-* format rather than usr_* format. This is expected — /user/create returns the existing user with a 200, and since that user was originally created via the legacy API, its user_token has simply become the new user_id.

    For full schema details, see Updates to user creation and identification and /user/create.

    Update Link token creation

    In /link/token/create, replace the top-level user_token field with user_id. For existing users, pass your stored user_token value in user_id. For new users, pass the user_id returned by /user/create. The user object is no longer required.

    The examples below show a CRA Link token. For Income, keep your existing products and income_verification configuration and make the same identifier change.

    Before migration
    curl -X POST https://sandbox.plaid.com/link/token/create \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "user": {
          "client_user_id": "client-user-id-12345"
        },
        "user_token": "user-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d",
        "products": ["cra_base_report", "cra_income_insights", "cra_network_insights"],
        "webhook": "${WEBHOOK_URL}",
        "client_name": "Name of App",
        "consumer_report_permissible_purpose": "ACCOUNT_REVIEW_CREDIT",
        "country_codes": ["US"],
        "language": "en",
        "cra_options": {
          "days_requested": 365,
          "base_report": {
            "client_report_id": "unique_base_report_id"
          }
        }
      }'
    After migration
    curl -X POST https://sandbox.plaid.com/link/token/create \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "user_id": "user-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d",
        "products": ["cra_base_report", "cra_income_insights", "cra_network_insights"],
        "webhook": "${WEBHOOK_URL}",
        "client_name": "Name of App",
        "consumer_report_permissible_purpose": "ACCOUNT_REVIEW_CREDIT",
        "country_codes": ["US"],
        "language": "en",
        "cra_options": {
          "days_requested": 365,
          "base_report": {
            "client_report_id": "unique_base_report_id"
          }
        }
      }'
    Update webhook handling

    Update your application to handle the renamed webhook events for the products you use:

    ProductLegacy webhookNew webhook
    CRACHECK_REPORT_READYUSER_CHECK_REPORT_READY
    CRACHECK_REPORT_FAILEDUSER_CHECK_REPORT_FAILED
    CRA Cash Flow InsightsINSIGHTS_UPDATED / LARGE_DEPOSIT_DETECTED / LOW_BALANCE_DETECTED / NEW_LOAN_PAYMENT_DETECTED / NSF_OVERDRAFT_DETECTEDCASH_FLOW_INSIGHTS_UPDATED
    IncomeINCOME_VERIFICATIONUSER_INCOME_VERIFICATION
    IncomeINCOME_VERIFICATION_RISK_SIGNALSUSER_INCOME_VERIFICATION_RISK_SIGNALS
    IncomeINCOME_VERIFICATION_REFRESH_RECONNECT_NEEDEDUSER_INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED

    The user_id field in the new webhooks is set to your stored user_token value for existing users, and to the usr_*-prefixed user_id for users created with the new User API.

    As of April 1, 2026, existing CRA customers on the legacy APIs automatically began receiving both the new and legacy versions of revised CRA webhooks in parallel. Existing Income integrations that have not migrated continue receiving the legacy Income webhooks. Add handlers for the new Income webhook codes before migrating, and retain legacy handlers while your integration still processes legacy webhook events. Recommended approach:

    • HTTP layer: Always return a 2xx status code for all incoming webhooks. If there is no 200 response or no response within 10 seconds, Plaid retries delivery for up to 24 hours. See webhook retries.
    • Application layer: Route events by webhook_type and webhook_code. Safely ignore any webhook_type values your application does not handle.
    Call product endpoints for existing users

    For existing CRA users, pass your stored user_token value in the user_id field when calling /cra/check_report/base_report/get, /cra/check_report/create, and other CRA endpoints. The response is identical — only the field name in the request changes.

    Before migration
    curl -X POST https://sandbox.plaid.com/cra/check_report/base_report/get \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "user_token": "user-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"
      }'
    After migration
    curl -X POST https://sandbox.plaid.com/cra/check_report/base_report/get \
      -H 'Content-Type: application/json' \
      -d '{
        "client_id": "${PLAID_CLIENT_ID}",
        "secret": "${PLAID_SECRET}",
        "user_id": "user-sandbox-b0e2c4ee-a763-4df5-bfe9-46a46bce993d"
      }'

    For new CRA users, pass the user_id returned by /user/create in the user_id field.

    For existing Income users, make the same field-name change when calling applicable Income endpoints, including /credit/sessions/get, /credit/bank_income/get, /credit/bank_income/pdf/get, /credit/bank_statements/uploads/get, /credit/payroll_income/get, /credit/employment/get, /credit/payroll_income/risk_signals/get, /credit/payroll_income/parsing_config/update, and /credit/payroll_income/refresh: pass the stored user_token value in the user_id field. For new Income users, pass the user_id returned by /user/create. See the Income API reference for endpoint details.

    Testing the migration

    You do not need to create new users to test the migrated API path. Any existing user's user_token can be used directly as the user_id in the new API fields.

    1. Pick any test user already created via the legacy /user/create endpoint. You'll have a stored user_token (format: user-sandbox-* in Sandbox, user-production-* in Production).
    2. Pass that user_token value into the user_id field in new API calls, for example:
      • /link/token/create → user_id field
      • For CRA, /cra/check_report/base_report/get or /cra/check_report/create → user_id field
      • For Income, /credit/payroll_income/get, /credit/bank_income/get, or another applicable Income endpoint → user_id field
    3. Listen for the new webhooks for your product and confirm the user_id field in the payload matches your stored user_token:
      • For CRA, USER_CHECK_REPORT_READY and USER_CHECK_REPORT_FAILED
      • For Income, USER_INCOME_VERIFICATION, USER_INCOME_VERIFICATION_RISK_SIGNALS, and USER_INCOME_VERIFICATION_REFRESH_RECONNECT_NEEDED, as applicable

    You can validate the full new flow end-to-end using only existing users in your system, without committing to production migration or creating new users.

    Compatibility: legacy users vs. new users

    Users created via the legacy /user/create endpoint (those with a user-* format user_token) are compatible with both the legacy and new APIs. Their user_token value can be passed into the old user_token fields or the new user_id fields interchangeably during migration. Note that the legacy user_id (an unprefixed string also returned by the old /user/create) is not the same as the user_token and is not compatible with either the legacy or new API fields — do not use it.

    Users created via the new /user/create (with with_upgraded_user: true) receive a user_id with a usr_* prefix. These users only work with the new APIs — you cannot pass their user_id into legacy user_token fields in /link/token/create, CRA endpoints, or Income endpoints. If you use the new /user/create flow in a test environment, make sure your code is already updated to use the new field names.

    Developer community
    GitHub
    GitHub
    Stack Overflow
    Stack Overflow
    YouTube
    YouTube
    Discord
    Discord