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

# Supercharge return-users

By the end of this guide, you'll know how to use Mesh Managed Tokens (MMT) to create a seamless return-user experience — so users who've already connected an account don't need to re-authenticate.

<Check>
  **Before you start**

  * You've set up `onIntegrationConnected` callback to capture `accessTokens` object (see [Use Mesh's callback functions](/build/callbacks))
</Check>

## Overview

Mesh's Managed Token system (MMT) simplifies how you manage user authentication tokens. MMT securely stores and manages token lifecycles on your behalf, giving you a persistent `tokenId` for each connected user account. You can use this to create a seamless return-user experience for exchanges and to interact with certain Mesh APIs — no manual token refresh or re-authentication required.

## Benefits

* **Streamlined UX**: After connecting their exchange account to your app, users can skip repetitive authentication steps on future visits.
* **Persistent Access**: Mesh handles all token refresh logic. For exchanges with long-lived tokens (like Coinbase), access stays valid indefinitely. For those with expiring tokens (like Binance), users will be prompted to re-authenticate when needed — but the `tokenId` you store never changes, so no backend updates are required on your part.
* **Integration-Agnostic**: Works across different auth methods (OAuth, credentials-based) with no added client-side complexity.

## How to Implement MMT

### 1. Obtain a `tokenId`

When a user authenticates with their exchange or wallet account, you will receive the SDK `onIntegrationConnected` event, which contains an object like this:

```json theme={null}
{
    "accessToken": {
        "accountTokens": [
            {
                "account": {
                    "meshAccountId": "<meshAccountId>",
                    "frontAccountId": "<frontAccountId>",
                    "accountId": "<accountId>",
                    "accountName": "<accountName>",
                    "fund": 0,
                    "cash": 0
                },
                "accessToken": "<accessToken>",
                "tokenId": "<tokenId>" // Use this
            }
        ],
        "brokerType": "<brokerType>", // Use this
        "brokerName": "<brokerName>",
        "brokerBrandInfo": {}
    }
}
```

Capture the `tokenId` and `brokerType`.

### 2. Build the `accessTokens` object

See the [Use Mesh's callback functions](/build/callbacks) guide for more information on how to use the `onIntegrationConnected` callback function to do this.

This will later be passed into the Mesh SDK for a returning user experience.

#### Notes & references:

* Store the `tokenId` securely, for example in a database. Make sure each `tokenId` and `brokerType` pair is clearly tied to the `userId` that connected the account.
* You'll notice that the `tokenId` & `accessToken` in the `integrationConnected` event are the same for the initial connection of a particular user account. But if the `accessToken` is ever refreshed or replaced, they will diverge. Link facilitates the user experience, and Mesh manages the token refresh logic for you, which means the `tokenId` will remain unchanged for a given `userId` + `brokerType` combination. **This means you should put the `tokenId` value from the SDK event into the `accessToken` field in this object**.
* The `brokerName`, `accountId`, and `accountName` fields are obsolete but must still be passed (can be empty) when initializing the SDK.

### 3. Pass the accessTokens object into the Mesh SDK

<AccordionGroup>
  <Accordion icon="https://mintcdn.com/mesh-40/Y-QnDCJ7fcNNqJ5o/images/icons/web.svg?fit=max&auto=format&n=Y-QnDCJ7fcNNqJ5o&q=85&s=6fd7faf20edd7e5cec27f3550deb55ef" title="Web SDK Example" width="24" height="24" data-path="images/icons/web.svg">
    ```javascript theme={null}
    const accessTokens: IntegrationAccessToken[] = [
      {
        accessToken: '<accessToken>', // Put the tokenId here
        brokerType: '<brokerType>', // Put the brokerType here
        brokerName: '',
        accountId: '',
        accountName: ''
      }
    ]
    ```

    This will then be used when initializing future sessions of the Mesh SDK for this user so they can skip re-authentication and proceed directly into the transfer or portfolio flow.

    ```javascript theme={null}
    const connection = createLink({
      renderType: 'embedded',
      theme: 'system',
      language: 'system',
      displayFiatCurrency: 'USD'
      accessTokens: accessTokens, // Refers to the accessTokens object
      onIntegrationConnected: payload => {
        // Payload contains accessTokens for newly connected accounts
        console.debug('[MESH LINK integration connected]', payload)
      },
    })

    connection.openLink(linkToken, 'custom-iframe-id')
    ```
  </Accordion>

  <Accordion icon="https://mintcdn.com/mesh-40/Y-QnDCJ7fcNNqJ5o/images/icons/apple.svg?fit=max&auto=format&n=Y-QnDCJ7fcNNqJ5o&q=85&s=3ea087c17aff5ef105e1f1e3cc39de58" title="iOS SDK Example" width="24" height="24" data-path="images/icons/apple.svg">
    ```swift theme={null}
    let accessTokens = [
        IntegrationAccessToken(
            accountId: "",
            accountName: "",
            accessToken: "<accessToken>", // Put the tokenId here
            brokerType: "<brokerType>", // Put the brokerType here
            brokerName: ""
        )
    ]
    ```

    This will then be used when initializing future sessions of the Mesh SDK for this user so they can skip re-authentication and proceed directly into the transfer or portfolio flow.

    ```swift theme={null}
    func launchMeshLink(
        linkToken: String,
        viewController: UIViewController,
        accessTokens: [IntegrationAccessToken]? = nil
    ) {
        var linkHandler: LinkHandler?

        let settings = LinkSettings(
            accessTokens: accessTokens, // Refers to the accessTokens object
            language: "system",
            displayFiatCurrency: "USD",
            theme: .system
        )

        // Called after a user connects an exchange, wallet, or other integration.
        // Payload contains accessTokens for newly connected accounts
        let onIntegrationConnected: (LinkPayload) -> Void = { linkPayload in
            switch linkPayload {
            case .accessToken(let accessTokenPayload):
                print(accessTokenPayload)
            case .delayedAuth(let delayedAuthPayload):
                print(delayedAuthPayload)
            @unknown default:
                print("unknown LinkPayload")
            }
        }

        let configuration = LinkConfiguration(
            linkToken: linkToken,
            settings: settings,
            disableDomainWhiteList: false,
            onIntegrationConnected: onIntegrationConnected,
        )

        let result = configuration.createHandler()

        // createHandler validates the configuration before Link is presented.
        switch result {
        case .success(let handler):
            linkHandler = handler
            // Present Link from the current UIViewController.
            handler.present(in: viewController)
        case .failure(let error):
            print(error)
        @unknown default:
            print("unknown LinkResult")
        }
    }
    ```
  </Accordion>

  <Accordion icon="https://mintcdn.com/mesh-40/Y-QnDCJ7fcNNqJ5o/images/icons/android.svg?fit=max&auto=format&n=Y-QnDCJ7fcNNqJ5o&q=85&s=0bbbb8fb34fea77c1a25aaa3c9d5dacc" title="Android SDK Example" width="24" height="24" data-path="images/icons/android.svg">
    ```kotlin theme={null}
    val accessTokens = listOf(
        IntegrationAccessToken(
            accountId = "",
            accountName = "",
            accessToken = "<accessToken>", // Put the tokenId here
            brokerType = "<brokerType>", // Put the brokerType here
            brokerName = "",
        ),
    )
    ```

    This will then be used when initializing future sessions of the Mesh SDK for this user so they can skip re-authentication and proceed directly into the transfer or portfolio flow.

    ```kotlin theme={null}
    // Register the Link launcher as an Activity or Fragment property
    private val linkLauncher = registerForActivityResult(LaunchLink()) { result ->
        when (result) {
            is LinkSuccess -> {
                // Called when Link returns payloads, such as a connected account or completed transfer.
                // Upon successful integration payload contains accessTokens for newly connected accounts
                Log.i(LOG_TAG, "LinkSuccess: ${result.payloads}")
            }
            is LinkExit -> {
                // Called when Link exits without payloads, including user exits or errors.
                Log.i(LOG_TAG, "LinkExit: ${result.errorMessage}")
            }
        }
    }

    private fun launchMeshLink(linkToken: String, accessTokens: List<IntegrationAccessToken>? = null) {
        // Create LinkConfiguration
        val configuration = LinkConfiguration(
            token = linkToken,
            theme = LinkTheme.SYSTEM,
            language = "system",
            displayFiatCurrency = "USD",
            accessTokens = accessTokens, // Refers to the accessTokens object
        )

        // Launch
        linkLauncher.launch(configuration)
    }
    ```
  </Accordion>

  <Accordion icon="https://mintcdn.com/mesh-40/Y-QnDCJ7fcNNqJ5o/images/icons/react.svg?fit=max&auto=format&n=Y-QnDCJ7fcNNqJ5o&q=85&s=162ed81498b01d342d25c427bd7bd5c7" title="React Native SDK Example" width="24" height="24" data-path="images/icons/react.svg">
    ```javascript theme={null}
    const accessTokens: IntegrationAccessToken[] = [
      {
        accessToken: '<accessToken>', // Put the tokenId here
        brokerType: '<brokerType>', // Put the brokerType here
        brokerName: '',
        accountId: '',
        accountName: ''
      }
    ]
    ```

    This will then be used when initializing future sessions of the Mesh SDK for this user so they can skip re-authentication and proceed directly into the transfer or portfolio flow.

    ```javascript theme={null}
    return (
      <LinkConnect
        linkToken={linkToken}
        settings={{
          language: 'system',
          displayFiatCurrency: 'USD',
          accessTokens, // Refers to the accessTokens object
        }}
        onIntegrationConnected={(payload: LinkPayload) => {
          // Payload contains accessTokens for newly connected accounts
          console.log('[MESH LINK integration connected]', payload);
        }}
      />
    );
    ```
  </Accordion>

  <Accordion icon="https://mintcdn.com/mesh-40/Y-QnDCJ7fcNNqJ5o/images/icons/flutter.svg?fit=max&auto=format&n=Y-QnDCJ7fcNNqJ5o&q=85&s=efe91e3117513e06731a5bbf7321efd0" title="Flutter SDK Example" width="24" height="24" data-path="images/icons/flutter.svg">
    ```dart theme={null}
    final accessTokens = [
      IntegrationAccessToken(
          accessToken: '<accessToken>', // Put the tokenId here
          brokerType: '<brokerType>', // Put the brokerType here
          brokerName: '',
          accountId: '',
          accountName: '',
        )
    ];
    ```

    This will then be used when initializing future sessions of the Mesh SDK for this user so they can skip re-authentication and proceed directly into the transfer or portfolio flow.

    ```dart theme={null}
    Future<void> _showMeshLinkPage(
        String linkToken, {
        List<IntegrationAccessToken> accessTokens = const [],
    }) async {
      // Show MeshSdk
      final result = await MeshSdk.show(
        context,
        configuration: MeshConfiguration(
          language: 'system',
          displayFiatCurrency: 'USD',
          theme: ThemeMode.system,
          integrationAccessTokens: accessTokens, // Refers to the accessTokens object
          linkToken: linkToken,
          onIntegrationConnected: (integration) {
            // Called after a user connects an exchange, wallet, or other integration.
            // Payload contains accessTokens for newly connected accounts
            print('Integration connected: $integration');
          },
        ),
      );

      // Handle the result
      switch (result) {
        case MeshSuccess():
          print('Mesh link finished successfully');
        case MeshError():
          print('Mesh link error: ${result.type}');
      }

      // Alternatively, use `when` method
      result.when(
        success: (success) {
          final payload = success.payload;
          print('Mesh link success: ${payload.page}');
        },
        error: (error) {
          final errorType = error.type;
          print('Mesh link error: $errorType');
        },
      );
    }
    ```
  </Accordion>
</AccordionGroup>

## Token Lifecycle and Behavior

| Scenario                                                                                                                                                    | Result                                                                                                              |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Same user reconnects with same integration**                                                                                                              | Returns same `tokenId`                                                                                              |
| **Different user connects to same integration**                                                                                                             | Returns a new `tokenId`                                                                                             |
| **Same user connects with different scopes (e.g. read vs. write)**                                                                                          | Returns distinct `tokenId` per scope                                                                                |
| **Write endpoint called using read-only TokenId**                                                                                                           | API returns a scope mismatch error                                                                                  |
| **Token revoked by client (use [Remove connection](https://docs.meshconnect.com/api-reference/managed-account-authentication/remove-connection) endpoint)** | Associated access is permanently disabled and Mesh also deletes the token physically, without any way to restore it |

## Supported Integrations

This list is growing and will be updated over time. **As new integrations are added, you will not have to update support on your end**.

* **Coinbase**
* **Binance**
* **Uphold**
* *Note: connections with self-custody wallets are maintained on subsequent sessions (unless the user actively kills the connection in their wallet app) without the need for handling tokens. This means the same smooth return user journey is achieved for wallet transactions.*

## Security Considerations

MMT streamlines token handling and unlocks some powerful functionality — here's how Mesh protects the data under the hood:

* **Encrypted Storage**: All tokens are encrypted at rest using modern encryption standards.
* **Scoped Access**: Each `tokenId` is tied to the permission scope (read or write) associated with the API key. Unauthorized operations (e.g., calling a write endpoint with a read-scoped token) will be rejected.
* **Client-Level Isolation**: Each `tokenId` is also scoped to a specific `clientId`. Even if the same end user connects the same integration account across multiple client apps, the tokens are isolated and not shared across clients.
* **User-Level Isolation**: Each `tokenId` is unique to a specific `endUserId` and integration.
* **Token Revocation**: Clients can revoke a `tokenId`, permanently disabling access and triggering a secure deletion process. There is no path to restore a revoked token.

## What's next

Next up: [Polish the experience](/build/polish) — with the core integration working, this guide walks you through the finishing touches: a well-designed entry point, clean session handling, and webhook setup.

***

<Accordion title="AI coding reference (llms.txt)">
  *AI coding reference — a compact summary of this page's APIs, parameters, and patterns for use by AI coding assistants (following the [llms.txt standard](https://llmstxt.org/)). Human readers can safely ignore this.*

  **llms.txt — Supercharge return-users (Mesh Managed Tokens)**

  Use Mesh Managed Tokens (MMT) for one-tap return-user flows. Mesh manages token refresh; you store a stable `tokenId`.

  **Step 1 — Capture from onIntegrationConnected**: Extract `accessToken.accountTokens[].tokenId` and `accessToken.brokerType`. Store server-side per `userId` + `brokerType`.

  **Step 2 — Build accessTokens array**:
  `[{ accessToken: '<tokenId>', brokerType: '<brokerType>', brokerName: '', accountId: '', accountName: '' }]`

  Critical: put `tokenId` in the `accessToken` field (not the raw accessToken string). `tokenId` is stable even when the underlying token rotates.

  **Step 3 — Pass into SDK on next session**: Include `accessTokens` array in `createLink()` / `LinkSettings` / `LinkConfiguration` / `MeshConfiguration`.

  **Token lifecycle**: Same user + integration → same `tokenId` | Different user or scope → new `tokenId` | Revoked → permanently disabled, unrestorable | Coinbase: tokens valid indefinitely | Binance: tokens expire, user re-auths but `tokenId` unchanged.

  **Supported integrations**: Coinbase, Binance, Uphold (growing). Self-custody wallet connections persist without tokens.

  **Security**: Tokens encrypted at rest; scoped per `clientId`, `endUserId`, and permission scope (read vs. write).
</Accordion>
