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
Instant Onboarding
  • Plaid Layer
Credit and Underwriting
  • Consumer Report (by Plaid Check)
  • Assets
  • Statements
  • Income
Fundamentals
  • Items
  • Accounts
  • Institutions
  • Sandbox
  • Link
  • Users
  • Consent
  • Network
  • OAuth
Partnerships
  • Processor tokens
  • Processor partners
  • Reseller partners
Plaid logo
Docs
Close search modal
Ask Bill!
Ask Bill!
Hi! I'm Bill! You can ask me all about the Plaid API. Try asking questions like:
    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.
    Plaid.com
    Log in
    Get API Keys
    Open nav

    Migrate to new User APIs

    Migration guide for existing Consumer Report integrations on legacy User APIs

    This guide is for customers who integrated with Consumer Report by Plaid Check (CRA) prior to December 2025. It explains how to migrate your integration from the legacy User APIs and CRA API endpoints (which use user_token as the primary identifier) to the new User APIs and updated CRA API endpoints. Migration is optional — your existing integration will continue to work, and there is currently no deadline to migrate.

    This migration guide applies only to Plaid Check Consumer Report (CRA) customers. If you use the legacy Plaid Income Verification product (non-CRA Bank Income), migration is not yet available. Contact your Account Manager for timing updates.

    =*=*=*=

    Are you on the legacy API?

    All clients who began using /user/create on or after December 10, 2025 are on the new User API by default and no migration is needed. 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 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 CRA 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 CRA 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 and 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 to listen for USER_CHECK_REPORT_READY and USER_CHECK_REPORT_FAILED instead of CHECK_REPORT_READY and CHECK_REPORT_FAILED. 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 CRA 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 integration 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
    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 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 you don't, 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.

    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", # your stored user_token value
        "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:

    Legacy webhookNew webhook
    CHECK_REPORT_READYUSER_CHECK_REPORT_READY
    CHECK_REPORT_FAILEDUSER_CHECK_REPORT_FAILED
    CASH_FLOW_UPDATES / INSIGHTS_UPDATED / LARGE_DEPOSIT_DETECTED / LOW_BALANCE_DETECTED / NEW_LOAN_PAYMENT_DETECTED / NSF_OVERDRAFT_DETECTEDCASH_FLOW_INSIGHTS_UPDATED

    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 customers on the legacy APIs automatically began receiving both the new and legacy versions of revised webhooks in parallel. This means you can update your webhook handling at your own pace — your existing integration continues to work throughout. Once you have switched to the new webhook events, you can safely ignore the legacy ones. 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.
    Retrieve reports for existing users

    For existing 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" # your stored user_token value
      }'

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

    =*=*=*=

    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
      • /cra/check_report/base_report/get → user_id field
      • /cra/check_report/create → user_id field
    3. Listen for USER_CHECK_REPORT_READY and USER_CHECK_REPORT_FAILED webhook events and confirm the user_id field in the payload matches your stored user_token.

    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 fields like user_token in /link/token/create or /cra/check_report/create. 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