> ## 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.

# Visão geral da vinculação social

> Como seus usuários conectam suas contas sociais — as três superfícies, a link session por trás de todas elas e as opções que elas compartilham.

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} />

Seus usuários conectam as próprias contas sociais eles mesmos — eles se autenticam diretamente em cada
rede, e você nunca vê nem armazena as credenciais deles. Tudo nesta página trata de levá-los até
esse momento e de descobrir como ele terminou.

Uma coisa é comum a todas as rotas: uma **link session**, criada com
[Criar uma Link Session](/docs/apis/profiles/create-link-session) a partir da sua API Key e de um `Profile-Key`.
Nada é assinado do seu lado e não há chave privada no fluxo.

## Três formas de conectar

Três formatos, e os dois primeiros são a mesma integração. Escolha por fluxo — eles não são
exclusivos, e muitas integrações usam a página hospedada para o onboarding e o widget dentro
do app depois disso.

| Superfície                                                                                                                         | O que seu usuário vê                                                           | White-label                                                                     | Escolha quando                                                                                                  |
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Widget — frames incorporados** ([mount](/docs/multiple-users/connect-widget#mount-a-slot))                                            | nossos botões inline no seu próprio layout; nenhum popup até o da própria rede | **O mais forte.** Sua página, suas fontes e cores, e seu usuário nunca sai dela | você tem um dashboard com uma linha por rede e quer que a vinculação aconteça ali mesmo. Max Pack.              |
| **Widget — seu próprio botão** ([popup](/docs/multiple-users/connect-widget#your-own-button))                                           | seu botão, depois um popup para a rede                                         | **Forte.** O popup é nosso, mas é breve e herda sua aparência                   | você quer seu próprio botão e estilo, e nenhum frame no seu layout. Max Pack.                                   |
| **Página de vinculação hospedada** ([como fazer](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | uma página que nós hospedamos, com seu logotipo, cores e CSS personalizado     | **O mais fraco.** É a nossa página, e seu usuário sai da sua para usá-la        | você quer um único link para distribuir, ou está fazendo onboarding por e-mail. Nada a construir, sem Max Pack. |

<Note>
  As duas linhas do widget são **uma integração**, não duas. Um único `init` dá a você as duas: monte frames
  onde quiser nossos botões e chame `popup()` a partir do seu próprio botão em todos os outros lugares. Elas
  compartilham uma sessão e reportam nos mesmos handlers.

  O [modo direto](/docs/multiple-users/connect-direct-mode) é o mesmo popup **sem** o nosso script — para
  uma página com uma Content-Security-Policy rígida, uma página renderizada no servidor ou um app nativo. Ali
  você mesmo abre e observa o popup.

  **Em dúvida?** Comece pela página de vinculação hospedada. Ela não precisa do Max Pack nem de parâmetros
  extras, e é a rota mais rápida para algo funcionando — migrar para o widget depois não muda como
  as sessões são criadas.
</Note>

## Criando um link

Chame [Criar uma Link Session](/docs/apis/profiles/create-link-session) com o `Profile-Key` do usuário no
cabeçalho. Para a página hospedada, essa é a requisição inteira:

```bash cURL theme={"system"}
curl -X POST https://api.ayrshare.com/api/profiles/link-sessions \
  -H "Authorization: Bearer $AYRSHARE_API_KEY" \
  -H "Profile-Key: $PROFILE_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Você recebe de volta uma `url` que carrega um token opaco e de curta duração:

```javascript Linking URL theme={"system"}
https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN
```

Você também pode [verificar se um link foi aberto](/docs/apis/profiles/get-link-session) e
[revogá-lo](/docs/apis/profiles/revoke-link-session) antes de expirar.

<Note>
  Este vídeo de um minuto mostra a criação de um link. Ele foi gravado antes das link sessions, então ainda
  mostra o envio de uma Private Key — esse passo não é mais necessário, e todo o resto que ele mostra
  permanece igual.

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

<h3 id="sending-the-linking-url">
  Enviar a URL de vinculação
</h3>

<Warning>
  Uma URL de vinculação autentica o seu usuário no perfil dele, então trate-a como uma senha. Envie-a por
  um canal confiável, não a registre em logs e não a passe a terceiros. Ela continua utilizável durante
  toda a sua janela, então um recarregamento ou uma nova tentativa de OAuth funciona — mas envie cada link
  para um único usuário e crie um link separado por pessoa.
</Warning>

### Abrir a URL de vinculação

Abra-a em uma nova aba do navegador, nova janela ou view controller. Você pode controlar o
[fechamento ou redirecionamento](/docs/multiple-users/api-integration-business#opening-and-closing-the-social-linking-url)
dessa janela.

<Note>
  As redes sociais não permitem que a página de vinculação hospedada seja aberta dentro de um iFrame, nem que a
  origem aprovada do parceiro `profile.ayrshare.com` seja ofuscada. Se você quer que a vinculação aconteça
  dentro da sua própria página, é para isso que existe o [widget incorporado](/docs/multiple-users/connect-widget):
  seus frames são servidos de uma origem Ayrshare e são a forma suportada de fazer isso.
</Note>

### Saber quando terminou

Dois sinais, e você pode usar qualquer um deles:

* **[Eventos de conclusão de vinculação](/docs/multiple-users/link-completion-events)** — defina um `origin` ao
  criar o link e a janela de vinculação envia `connect:success`, `connect:error` e
  `connect:cancelled` para a sua página conforme acontecem. Sem polling.
* **[Consultar uma Link Session](/docs/apis/profiles/get-link-session)** — informa `completedAt`,
  `lastCompletedAt` e `completedNetworks`. Este é o sinal para apps nativos, e o único
  para o Telegram, que conclui fora do fluxo.

<h2 id="jwt-expires-in">
  Expiração do link
</h2>

Um link é válido por **5 minutos** por padrão. Depois disso, crie um novo.

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

Com o Max Pack, defina `expiresIn` em minutos para ampliar essa janela — até **2880 minutos
(48 horas)**, que é o máximo que a API aceita:

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

Uma janela mais longa é o que torna prático [enviar o link por e-mail](#connect-accounts-email) — um usuário
reconectando uma conta que caiu pode ir direto do seu e-mail para a rede, sem visitar
seu app primeiro.

<Warning>
  Revise com sua equipe de segurança por quanto tempo um link deve permanecer ativo. Uma janela mais longa
  é um período mais longo em que um link interceptado ainda funciona. Se um deles vazar, você pode
  [revogá-lo](/docs/apis/profiles/revoke-link-session) em vez de esperar que expire.
</Warning>

## Profile Key

O `Profile-Key` indica a qual User Profile o link se refere. Você o encontra no painel de
desenvolvedor da Ayrshare alternando para esse perfil.

<Note>
  **A Private Key não é mais usada.** Os links não são assinados, portanto não há nada para ler de um
  arquivo nem para colar no seu código. O parâmetro legado `privateKey` continua sendo aceito e ignorado, então
  integrações existentes seguem funcionando, e o arquivo `private.key` do seu Integration Package pode
  ficar sem uso.
</Note>

## Alternar entre profiles

Se um profile já está logado, abrir o link de outro profile não troca de profile — isso é
deliberado e mantém a experiência rápida para um usuário que já está lá. Para forçar a troca,
consulte
[Logout automático de uma sessão de profile](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session).

<h2 id="instagram-link-method">
  Método de vinculação do Instagram
</h2>

Contas do Instagram podem ser vinculadas de [duas formas](/docs/dashboard/connect-social-accounts/instagram):
diretamente via **Instagram Login** ou por meio de uma **Página do Facebook conectada**. Qual fluxo é
iniciado quando um usuário clica no botão do Instagram normalmente é controlado pela configuração de
[Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) em nível de conta.

O parâmetro do corpo `instagramLinkMethod` sobrescreve essa configuração para um único link:

| Valor       | Fluxo de vinculação do Instagram                                 |
| ----------- | ---------------------------------------------------------------- |
| `instagram` | Instagram Login direto. Não é necessária uma Página do Facebook. |
| `facebook`  | Vincular por meio de uma Página do Facebook conectada.           |

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

O override se aplica durante toda a vida desse link, inclusive ao longo do redirecionamento de
autorização do Instagram/Facebook. Algumas coisas a saber:

* Ele não altera sua configuração em nível de conta nem afeta nenhum outro link.
* Omita-o e a configuração em nível de conta se aplica, exatamente como antes.
* Um valor inválido retorna `400` listando os valores válidos (`instagram`, `facebook`).
* Revise as
  [diferenças entre os recursos](/docs/multiple-users/manage-user-profiles#direct-instagram-login-vs-facebook-page-authentication)
  antes de escolher — alguns recursos do Instagram, como pesquisa de hashtag e colaborações, estão
  disponíveis apenas com a autenticação via Página do Facebook.

<h2 id="connect-accounts-email">
  E-mail de conexão de contas
</h2>

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

A Ayrshare pode enviar o link por e-mail ao seu usuário por você, para que ele chegue à sua página de
vinculação sem visitar seu app. Combine com um [`expiresIn`](#jwt-expires-in) mais longo — os cinco
minutos padrão raramente sobrevivem a uma caixa de entrada.

### JSON de conexão de contas

**Todos os campos dentro de `email` são obrigatórios.** Um campo ausente faz o envio falhar.

```json Example Contact Email Request theme={"system"}
{
  "expiresIn": 60,
  "email": {
    "to": "john@user.com",
    "contactEmail": "support@mycompany.com",
    "company": "ACME",
    "termsUrl": "https://www.ayrshare.com/terms",
    "privacyUrl": "https://www.ayrshare.com/privacy"
  }
}
```

<Warning>
  `expiresIn` é um parâmetro de **nível superior**, não parte do objeto `email`. Aninhado dentro de `email`,
  ele é ignorado, e seu usuário recebe um link que expira em cinco minutos.
</Warning>

A resposta informa o resultado em `emailSent`:

```json Example Contact Email Response theme={"system"}
{
  "status": "success",
  "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
  "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN",
  "expiresAt": "2026-09-02T09:03:26.838Z",
  "emailSent": true,
  "title": "Acme Client"
}
```

Uma **falha** no envio não volta como `emailSent: false` — ela retorna `code: 333` em vez disso. Então
`false` significa que nenhum e-mail foi solicitado.

### Exemplo de e-mail Connect Accounts

Aqui está um exemplo do e-mail que abre a página de vinculação social:

<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" />

O e-mail virá do endereço:

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

<h2 id="mobile-jwt">
  Apps mobile
</h2>

Abra a URL de vinculação no **navegador do sistema**, nunca em um webview incorporado: o Google rejeita o
login em um deles com `disallowed_useragent`, e a Meta o bloqueia de imediato. Seu usuário veria a página
de erro da própria rede, e nada do seu lado corrige isso.

* **iOS** — `ASWebAuthenticationSession` ou `SFSafariViewController`.
* **Android** — Chrome Custom Tabs.

Como um app nativo não tem janela de navegador para receber eventos, obtenha o resultado com
[Consultar uma Link Session](/docs/apis/profiles/get-link-session). Defina `origin` como o seu esquema
personalizado (`myapp://connected`) para que a página tenha um caminho de volta para o seu app.

<h3 id="mobile-code-examples">
  Exemplos de código para mobile
</h3>

Substitua `linkingURL` pela `url` retornada por
[Criar uma Link Session](/docs/apis/profiles/create-link-session).

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

  class ViewController: UIViewController, SFSafariViewControllerDelegate {

      var linkingURL = "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme"

      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: linkingURL) {
              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 linkingURL = "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme";

    @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(linkingURL)) {
        await launch(linkingURL);
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Could not launch $linkingURL'),
          ),
        );
      }
    }
  }
  ```

  ```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 linkingURL = 'https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme';

    const openURLInBrowser = async () => {
      try {
        await WebBrowser.openBrowserAsync(linkingURL);
        // 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>

## Testando

É **recomendado** primeiro criar um link no [Postman](/docs/testing/postman). Seu Integration
Package — na página de API Key do Primary Profile no dashboard — inclui uma configuração de exemplo do Postman.
Importe-a, preencha seu Profile Key no campo *body* `profileKey` e clique em *Send*.

A configuração de exemplo ainda preenche `privateKey` e `domain`. `privateKey` é ignorado, e você pode
limpar `domain` a menos que sua conta tenha mais de um domínio de vinculação.

Você também pode [gerar o código pelo Postman](/docs/testing/postman#auto-generate-api-code-with-postman).

### Bubble.io

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

## Legado: generateJWT

<Info>
  [Gerar uma URL de vinculação](/docs/apis/profiles/generate-jwt) (`generateJWT`) faz o mesmo trabalho e está
  **obsoleto** — totalmente suportado, sem data de remoção e sem alteração para os links que você já
  distribuiu. A página dele documenta seus parâmetros, incluindo os três que agora são aceitos e
  ignorados.

  Os dois endpoints executam o mesmo validador, então tudo nesta página se aplica a qualquer um dos dois. A
  única diferença que vale conhecer ao migrar: o `generateJWT` tolera três coisas que Criar uma Link
  Session rejeita — uma rede `allowedSocial` não reconhecida, uma credencial do X sozinha e um
  `redirect` que não seja string.
</Info>
