Backend token exchange
Migrate existing connections to Core Exchange without requiring users to reauthenticate
Is this for you?
Backend token exchange lets Plaid migrate an existing Plaid Exchange connection to Core Exchange server to server, without sending the user back through your OAuth flow. For the rest of what that move involves, see Migrating from Plaid Exchange.
This isn't a self-serve Dashboard feature: your Plaid contact configures your exchange endpoint and migration cohort internally, so raise it with them before you begin planning a migration. This page describes what your endpoint needs to do once that's set up, not how to request it.
How it works
Plaid calls a token exchange endpoint you provide, sending the access or auth token from the user's existing connection. Your endpoint validates that token, identifies the user, and returns a set of Core Exchange OAuth tokens, without the user taking any action.
The path Plaid calls is whatever endpoint your Plaid contact configures for your institution; there's no fixed route every institution must implement. Examples in this guide use /token as a placeholder.
Request
Plaid sends a POST request with a JSON body containing a single field:
| Field | Description |
|---|---|
token | The user's existing Plaid Exchange auth token for the connection being migrated. |
curl -X POST 'https://your-institution.com/token' \
-H 'Content-Type: application/json' \
-d '{
"token": "eyJhbGciOiJI..."
}'Authenticating the request
Plaid sends no OAuth client credentials to this endpoint. It presents a client certificate if your institution is configured for mTLS, along with any headers configured for your institution; your Plaid Exchange X-PLAID-CLIENT-ID and X-PLAID-SECRET aren't sent automatically. Tell your Plaid contact what your endpoint expects.
Response
Return these fields. access_token, refresh_token, and expires_in work the same way they do in a standard OAuth token exchange, but this response carries token_type and a direct user_id instead of an id_token:
| Field | Description |
|---|---|
access_token | An opaque string. Plaid presents this as a bearer token to your Core Exchange API. |
token_type | The token type, as in a standard OAuth token response. |
refresh_token | An opaque, revocable token Plaid uses to request new access tokens on an ongoing basis. |
expires_in | The lifetime of the access token, in seconds. |
scope | Optional. The granted scope, if your integration uses scoped tokens. |
user_id | A stable, opaque identifier for the user, matching the consistency key your integration returns elsewhere. |
{
"access_token": "agstynmdygjdghabrgraeh...",
"token_type": "bearer",
"refresh_token": "dhcsrtjsrgayvkdisfdgntshstu...",
"expires_in": 900,
"user_id": "2347456437346745"
}Errors
Return errors in the standard OAuth format used elsewhere in Core Exchange: an error string from the token error codes, plus an optional error_description. Return 401 if you can't validate the token or map it to a user.
{
"error": "invalid_grant",
"error_description": "Token has expired"
}Handling repeated and retried requests
A single user can have several connections to different apps, each backed by the same underlying legacy token. Plaid calls your endpoint once per connection, so expect repeated calls presenting the same token.
- Don't deactivate the original token on first use. Subsequent calls with that same token, for other connections, need to keep working.
- Don't treat a repeated or invalid token as a security event. Failed or duplicate exchange attempts during a migration are expected traffic, not signals of compromise; triggering account locks or user-facing security notifications from this endpoint will interrupt migrations that would otherwise succeed.
- Make retrying safe. Plaid retries
500,502,503, and504responses with the same token. Exchanging the same valid token twice should return usable tokens, not an error.
After migrating
Once a connection has been exchanged, Plaid uses the returned access_token and refresh_token the same way it would for any Core Exchange connection established through the standard OAuth flow. See Implementing the OAuth flow for how Plaid uses refresh_token, and the migration guide for how this fits into a broader migration timeline.