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

# Linking URL Overview (generateJWT)

> Generate a social network linking URL for a user profile.

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["business"]} maxPackRequired={false} />

The [generateJWT endpoint](/docs/apis/profiles/generate-jwt) generates a social network linking URL for a single User Profile.
See the [Business Plan and Launch Plan API integration](/docs/multiple-users/api-integration-business) for more details.

<Info>
  **`generateJWT` is no longer the only way to create a linking URL.** It still works and this
  page still describes it, but for new integrations use
  [Create a Link Session](/docs/apis/profiles/create-link-session) — it needs no private key, and it lets you
  [check whether a link has been opened](/docs/apis/profiles/get-link-session) or
  [revoke it](/docs/apis/profiles/revoke-link-session) before it expires.

  Both endpoints run the same validator, so everything on this page about `expiresIn`,
  `allowedSocial`, `instagramLinkMethod`, `redirect`, `logout` and the Connect Accounts email
  applies to either one. `generateJWT` keeps three small tolerances the newer endpoint rejects:
  an unrecognised `allowedSocial` network, a lone X credential, and a non-string `redirect`.
</Info>

## Sending the Linking URL

<Warning>
  A linking URL signs your user into their profile, so treat it like a password. Send it over
  a channel you trust, don't log it, and don't pass it on to a third party. It stays usable for
  its whole window, so a reload or an OAuth retry works — but send each link to one user only,
  and create a separate link per person.
</Warning>

## Switching Profiles

To switch between different profile sessions (for example, when testing with multiple profiles), you'll need to log out the current profile first. See [Automatic Logout of a Profile Session](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session) for instructions on how to properly handle profile switching.

## Profile Key

The `profileKey` identifies which User Profile the linking URL is for. Find it in the Ayrshare developer dashboard by switching to that profile.

<Note>
  The Private Key is no longer used. Linking URLs are not signed, so there is nothing to read from a file or paste into your code. `privateKey` is still accepted and ignored, so existing integrations keep working, and the `private.key` file in your Integration Package can be left unused.
</Note>

## Generate a Linking URL

1 minute video showing how to create a linking URL. It was recorded before link sessions, so it still shows a Private Key being sent; that step is no longer needed.

<div class="video-container">
  <iframe width="380" height="200" src="https://www.youtube.com/embed/JI232HBWHWc" title="Generate a JSON Web Token" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" />
</div>

The linking URL is valid for **5 minutes**. After 5 minutes you must generate a new linking URL.
See the [Max Pack `expiresIn`](/docs/apis/profiles/generate-jwt-overview#jwt-expires-in) for additional options.

## Opening the Linking URL

Open the linking URL in a new browser tab, browser window, or View Controller on iOS.

You may control the [closing or redirecting](/docs/multiple-users/api-integration-business#opening-and-closing-the-social-linking-url) of the new window or tab.

<Note>
  The social networks do not allow opening the URL in an iFrame or obfuscating the approved partner
  origin domain profile.ayrshare.com.
</Note>

## Verifying the URL

`verify: true` is no longer used. It is accepted and ignored.

There is no signed token to verify: the returned `token` is checked by the linking page when your user opens the URL. This option used to re-parse the JWT with your Private Key, to catch a corrupt key before you sent the URL out. With no key in the flow, that failure mode is gone.

## Testing in Postman

It is **recommended** to first test the linking URL creation in [Postman](/docs/testing/postman).
Included in the Integration Package, found in the Primary Profile API Key page of the dashboard, is a sample Postman config JSON file.
Just import the config file into Postman, fill in your Profile Key (found in the Ayrshare developer dashboard by switching to the profile you want to test) in the `profileKey` *body* field, and click the blue *Send* button.
The sample config still pre-fills `privateKey` and `domain`. `privateKey` is ignored, and you can clear `domain` unless your account has more than one linking domain.

You can also [generate the code from Postman](/docs/testing/postman#auto-generate-api-code-with-postman).

## Instagram Link Method

Instagram accounts can be linked in [two ways](/docs/dashboard/connect-social-accounts/instagram): directly with **Instagram Login**, or via a **connected Facebook Page**.
Which flow starts when a user clicks the Instagram button on the social linking page is normally controlled by the account-wide [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) setting in the dashboard.

The `instagramLinkMethod` body parameter of the [generateJWT endpoint](/docs/apis/profiles/generate-jwt) lets you override that setting for a single linking URL:

| Value       | Instagram Linking Flow                             |
| ----------- | -------------------------------------------------- |
| `instagram` | Direct Instagram Login. No Facebook Page required. |
| `facebook`  | Link via a connected Facebook Page.                |

For example, if your account default is Facebook Page linking, the following forces direct Instagram Login for just this linking session:

```json Instagram Link Method theme={"system"}
{
  "instagramLinkMethod": "instagram"
}
```

The generated linking URL will include the override, and clicking the Instagram button on the linking page will start the requested flow for the duration of that session — including across the Instagram/Facebook authorization redirect.

A few things to know:

* The override only applies to the linking page opened from the returned URL. It does not change your account-wide Instagram Login setting or affect other linking sessions.
* If `instagramLinkMethod` is omitted, the linking page uses your account-wide setting, exactly as before.
* If an invalid value is sent, a `400` error is returned listing the valid values (`instagram`, `facebook`).
* Review the [feature differences](/docs/multiple-users/manage-user-profiles#direct-instagram-login-vs-facebook-page-authentication) between the two flows before choosing an override — some Instagram features, such as hashtag search and collaborations, are only available with Facebook Page authentication.

## JWT Expires In

<PlansAvailable plans={["business"]} maxPackRequired />

If you want a longer link timeout than the default 5 minutes, include the `expiresIn` field.

For example, send the following JSON to set the linking URL valid for 30 minutes:

```json JWT Expires In theme={"system"}
{
  "expiresIn": 30
}
```

This allows you to [email the link](/docs/apis/profiles/generate-jwt-overview#connect-accounts-email) to your users instead of them having to go to your app or platform.
A common use case is when your user needs to reconnect a social account, you can email them the JWT link to directly re-link the social account instead of having to navigate to your platform.

<Warning>
  Be sure to review with your security team how long your business wants to keep the JWT alive.
  Longer expire times create additional risk of an unauthorized party accessing the link.
</Warning>

## Integrations

### Bubble.io

If you are a Bubble user, please see *Generate a Linking URL in Bubble* in the Bubble.io section for instructions:

<Card title="Bubble linking URL" icon="link" href="/docs/packages-guides/bubble#generate-a-linking-url-in-bubble" horizontal />

### Mobile JWT

The following Swift, Flutter, and React Native [mobile code examples](/docs/apis/profiles/generate-jwt-overview#mobile-code-examples) show how to launch the social linking page on an iOS device.
Replace the `jwtURL` String variable with the return from the [/generateJWT endpoint](/docs/apis/profiles/generate-jwt).

#### Swift (iOS)

In Swift, use a `UIViewController` and `SFSafariViewControllerDelegate`.
We don't recommend using a `WebView` since some social networks such as Facebook and Google block authentication.

#### Flutter (Dart)

In Flutter (Dart), there is no direct equivalent to a `UIViewController` or the `SFSafariViewController`.
However, you can achieve a similar functionality by using the `url_launcher` package to open web URLs.

#### React Native

React Native also doesn't have a direct equivalent to `SFSafariViewController`, but you can achieve a similar result with the `WebBrowser` API provided by `expo-web-browser`, which opens a URL in a modal browser window that shares cookies with the system browser. Otherwise, you can use the built-in React Native `Linking` function to open Safari: `await Linking.canOpenURL(jwtURL);`

#### Mobile Code Examples

<CodeGroup>
  ```swift Swift theme={"system"}
  import UIKit
  import SafariServices

  class ViewController: UIViewController, SFSafariViewControllerDelegate {
      
      var jwtURL = "https://profile.ayrshare.com?domain=acme&jwt=eyJhbGciOiJ"

      override func viewDidLoad() {
          super.viewDidLoad()
          setupButton()
      }
      
      func setupButton() {
          let button = UIButton(type: .system)
          button.frame = CGRect(x: (view.bounds.width - 200) / 2, y: (view.bounds.height - 50) / 2, width: 200, height: 50)
          button.setTitle("Open URL", for: .normal)
          button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
          view.addSubview(button)
      }

      @objc func buttonTapped() {
          openURLInInAppBrowser()
      }

      func openURLInInAppBrowser() {
          if let url = URL(string: jwtURL) {
              let safariVC = SFSafariViewController(url: url)
              safariVC.delegate = self
              present(safariVC, animated: true, completion: nil)
          }
      }

      // Optional: If you want to handle when the in-app browser is closed
      func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
          controller.dismiss(animated: true, completion: nil)
      }
  }
  ```

  ```dart Flutter theme={"system"}
  /** yaml dependencies
    dependencies:
      flutter:
        sdk: flutter
      url_launcher: ^6.2.1
  */

  import 'package:flutter/material.dart';
  import 'package:url_launcher/url_launcher.dart';

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'URL Launcher Example',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      );
    }
  }

  class MyHomePage extends StatelessWidget {
    final String jwtURL = "https://profile.ayrshare.com?domain=acme&jwt=eyJhbGciOiJ";

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('URL Launcher Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              openURLInBrowser(context);
            },
            child: Text('Open URL'),
          ),
        ),
      );
    }

    void openURLInBrowser(BuildContext context) async {
      if (await canLaunch(jwtURL)) {
        await launch(jwtURL);
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Could not launch $jwtURL'),
          ),
        );
      }
    }
  }
  ```

  ```jsx React Native theme={"system"}
  /**
  • Using the API provided by expo-web-browser,
  • which opens a URL in a modal browser window that shares cookies
  • with the system browser.

  • Learn more about expo: https://reactnative.dev/docs/environment-setup?guide=quickstart
  • and running the following command:
  • expo install expo-web-browser
  */

  import React from 'react';
  import { StyleSheet, Button, View } from 'react-native';
  import * as WebBrowser from 'expo-web-browser';

  export default function App() {
    const jwtURL = 'https://profile.ayrshare.com?domain=acme&jwt=eyJhbGciOiJ';

    const openURLInBrowser = async () => {
      try {
        await WebBrowser.openBrowserAsync(jwtURL);
        // Optional: WebBrowser.openBrowserAsync returns a promise that resolves with an object containing
        // 'type' that can be 'cancelled' or 'dismissed'. You can use this to handle when the browser is closed.
      } catch (error) {
        console.error(error);
      }
    };

    return (
      <View style={styles.container}>
        <Button title="Open URL" onPress={openURLInBrowser} />
      </View>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
    },
  });
  ```
</CodeGroup>

## Connect Accounts Email

<max_pack />

In conjunction with the longer expire time option, you can also automatically have Ayrshare email your users a link to the social linkage page.

### Connect Accounts JSON

For example the following JSON will send an email to `john@user.com` with the company name ACME, contact email `support@mycompany.com`, and links to the terms and privacy policy:

```json Example Contact Email Request theme={"system"}
/**
  All fields are in the email object required.
  Missing fields will cause the email to fail.
*/
{
  "email": {
    "to": "john@user.com",
    "contactEmail": "support@mycompany.com",
    "company": "ACME",
    "termsUrl": "https://www.ayrshare.com/terms",
    "privacyUrl": "https://www.ayrshare.com/privacy",
    "expiresIn": 60
  }
}
```

The response will include the following if the email and expire time was set:

```json Example Contact Email Response theme={"system"}
{
  "emailSent": true,
  "expiresIn": "30m"
}
```

### Connect Accounts Email Example

Here is an example of an email with the Connect Account link that opens social linkage page:

<img src="https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/profiles/jwt-email.webp?fit=max&auto=format&n=Nmrhj2Gh7WSf62Bh&q=85&s=fbe4ee86ca59c26a5bd5b289fda96b8b" alt="Connect Accounts email" width="563" class="center" data-path="images/apis/profiles/jwt-email.webp" />

The email will come from the address:

`Social Connect Hub <connect@socialconnecthub.com>`
