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

# ソーシャル連携の概要

> ユーザーがソーシャルアカウントを連携する方法 — 3 つのサーフェス、そのすべての背後にあるリンクセッション、そして共通のオプション。

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

ユーザーは自分自身のソーシャルアカウントを自分で連携します — 各ネットワークで直接認証するため、
開発者がユーザーの認証情報を目にしたり保存したりすることはありません。このページで扱うのは、
ユーザーをその瞬間まで導き、結果がどうだったかを知るためのすべてです。

すべてのルートに共通するものが 1 つあります: **リンクセッション**です。API Key と `Profile-Key` から
[Link Session の作成](/docs/apis/profiles/create-link-session)で作成します。お客様側で署名するものはなく、
フローに秘密鍵は登場しません。

## 3 つの連携方法

形は 3 つありますが、最初の 2 つは同じ統合です。フローごとに選択してください — 排他的なものでは
なく、オンボーディングにはホスト型リンクページを、その後のアプリ内ではウィジェットを使う統合も
多数あります。

| サーフェス                                                                                                  | ユーザーに見えるもの                                                  | ホワイトラベル度                                    | 選ぶべき場面                                                          |
| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------- |
| **ウィジェット — 埋め込みフレーム**([mount](/docs/multiple-users/connect-widget#mount-a-slot))                            | 貴社レイアウト内にインラインで表示される当社のボタン。ネットワーク自身のポップアップまで、ポップアップは表示されません | **最も強力。** 貴社のページ、貴社のフォントと配色で、ユーザーはページを離れません | ネットワークごとの行を持つダッシュボードがあり、その場で連携を完結させたい場合。Max Pack が必要です。         |
| **ウィジェット — 独自のボタン**([popup](/docs/multiple-users/connect-widget#your-own-button))                           | 貴社のボタン、その後ネットワーク用のポップアップが 1 つ                               | **強力。** ポップアップは当社のものですが、短時間で、貴社の外観を継承します    | 独自のボタンとスタイルを使い、レイアウトにフレームを置きたくない場合。Max Pack が必要です。              |
| **ホスト型リンクページ**([使い方](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | 当社がホストするページ。貴社のロゴ、カラー、カスタム CSS が反映されます                      | **最も弱い。** 当社のページであり、ユーザーは貴社のページを離れて使用します    | 配布するリンクを 1 つ用意したい場合や、メールでオンボーディングする場合。構築するものはなく、Max Pack も不要です。 |

<Note>
  ウィジェットの 2 つの行は 2 つの統合ではなく、**1 つの統合**です。1 回の `init` で両方が使えます:
  当社のボタンを置きたい場所にフレームをマウントし、それ以外の場所では独自のボタンから `popup()` を
  呼び出します。両者は 1 つのセッションを共有し、同じハンドラーに結果を報告します。

  [ダイレクトモード(Direct Mode)](/docs/multiple-users/connect-direct-mode)は、当社のスクリプト**なし**の
  同じポップアップです — 厳格な Content-Security-Policy を持つページ、サーバーレンダリングされる
  ページ、ネイティブアプリ向けで、この場合はポップアップを自分で開いて監視します。

  **迷ったら?** ホスト型リンクページから始めてください。Max Pack も追加パラメータも不要で、動くものに
  到達する最速のルートです — 後からウィジェットに移行しても、セッションの作成方法は変わりません。
</Note>

## リンクの作成

ヘッダーにユーザーの `Profile-Key` を指定して [Link Session の作成](/docs/apis/profiles/create-link-session)を
呼び出します。ホスト型ページの場合、リクエストはこれだけです:

```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 '{}'
```

短時間だけ有効な不透明なトークンを含む `url` が返されます:

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

[リンクが開かれたかどうかの確認](/docs/apis/profiles/get-link-session)や、期限切れ前の
[失効](/docs/apis/profiles/revoke-link-session)も行えます。

<Note>
  この 1 分間の動画はリンクの作成手順を紹介しています。Link Sessions より前に収録されたため、
  Private Key を送信する様子が残っていますが、その手順はもう必要ありません。それ以外の内容は
  変わっていません。

  <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">
  リンク用 URL の送信
</h3>

<Warning>
  リンク用 URL はユーザーをそのプロファイルにサインインさせるため、パスワードと同様に扱ってください。
  信頼できる経路で送り、ログに残さず、第三者に渡さないでください。有効期間中は使い続けられるので
  リロードや OAuth の再試行は問題ありませんが、各リンクは 1 人のユーザーにのみ送り、人ごとに個別の
  リンクを作成してください。
</Warning>

### リンク用 URL を開く

リンク用 URL は、新しいブラウザタブ、ウィンドウ、または View Controller で開きます。そのウィンドウの
[閉じる、またはリダイレクトする動作](/docs/multiple-users/api-integration-business#opening-and-closing-the-social-linking-url)
を制御することができます。

<Note>
  ソーシャルネットワークは、ホスト型リンクページを iFrame 内で開いたり、承認済みのパートナー
  オリジン `profile.ayrshare.com` を難読化することを許可していません。連携を自身のページ内で
  完結させたい場合は、そのために [埋め込みウィジェット](/docs/multiple-users/connect-widget)があります:
  ウィジェットのフレームは Ayrshare のオリジンから配信され、これがサポートされている方法です。
</Note>

### 完了したことを知る

シグナルは 2 つあり、どちらでも使えます:

* **[リンク完了イベント](/docs/multiple-users/link-completion-events)** — リンク作成時に `origin` を設定
  すると、連携ウィンドウが `connect:success`、`connect:error`、`connect:cancelled` を発生と同時に
  貴社のページへ post します。ポーリングは不要です。
* **[Link Session の取得](/docs/apis/profiles/get-link-session)** — `completedAt`、`lastCompletedAt`、
  `completedNetworks` を報告します。ネイティブアプリ向けのシグナルであり、帯域外で完了する
  Telegram にとっては唯一のシグナルです。

<h2 id="jwt-expires-in">
  リンクの有効期限
</h2>

リンクはデフォルトで **5 分間** 有効です。それを過ぎたら、新しいリンクを作成してください。

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

Max Pack があれば、`expiresIn` を分単位で設定して有効期間を広げられます — API が受け付ける最大値は
\*\*2880 分(48 時間)\*\*です:

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

有効期間を長くすることで、[リンクのメール送信](#connect-accounts-email)が現実的になります —
接続が切れたアカウントを再接続するユーザーは、まず貴社のアプリを訪れることなく、メールから直接
ネットワークへ進めます。

<Warning>
  リンクをどれだけの期間有効にしておくべきかは、セキュリティチームと必ず確認してください。
  有効期間が長いほど、傍受されたリンクが使える期間も長くなります。リンクが流出した場合は、
  期限切れを待つのではなく[失効](/docs/apis/profiles/revoke-link-session)させることができます。
</Warning>

## Profile Key

`Profile-Key` は、リンクがどの User Profile のものかを示します。Ayrshare の開発者ダッシュボードで
そのプロファイルに切り替えると確認できます。

<Note>
  **Private Key は使用されなくなりました。** リンクは署名されないため、ファイルから読み込んだり
  コードに貼り付けたりするものはありません。レガシーの `privateKey` パラメータは引き続き受け付け
  られて無視されるので既存の連携はそのまま動作し、Integration Package の `private.key` ファイルは
  使わないままで構いません。
</Note>

## プロファイルの切り替え

プロファイルがすでにサインインしている場合、別のプロファイルのリンクを開いてもプロファイルは
切り替わりません — これは意図的なもので、すでにサインインしているユーザーの体験を高速に保つため
です。強制的に切り替えるには、
[Automatic Logout of a Profile Session](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session)
を参照してください。

<h2 id="instagram-link-method">
  Instagram のリンク方式
</h2>

Instagram アカウントは [2 つの方法](/docs/dashboard/connect-social-accounts/instagram)でリンクできます:
**Instagram Login** を用いて直接リンクする方法と、**接続された Facebook Page** を経由する方法です。
ユーザーが Instagram ボタンをクリックしたときにどちらのフローが開始されるかは、通常、アカウント
全体の [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) 設定によって制御されます。

`instagramLinkMethod` ボディパラメータを使用すると、単一のリンクに対してその設定を上書きできます:

| 値           | Instagram のリンクフロー                             |
| ----------- | --------------------------------------------- |
| `instagram` | Instagram Login による直接リンク。Facebook Page は不要です。 |
| `facebook`  | 接続された Facebook Page 経由でのリンク。                  |

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

この上書きは、Instagram/Facebook の認可リダイレクトを跨ぐ場合も含めて、そのリンクの有効期間中
適用されます。いくつかの注意点:

* アカウント全体の設定を変更したり、他のリンクに影響を与えたりしません。
* 省略した場合は、これまでどおりアカウント全体の設定が適用されます。
* 無効な値は、有効な値(`instagram`、`facebook`)を列挙した `400` を返します。
* 上書きを選択する前に、
  [機能の違い](/docs/multiple-users/manage-user-profiles#direct-instagram-login-vs-facebook-page-authentication)
  を確認してください — ハッシュタグ検索やコラボレーションなど、一部の Instagram 機能は
  Facebook Page 認証でのみ利用可能です。

<h2 id="connect-accounts-email">
  Connect Accounts メール
</h2>

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

Ayrshare がリンクをユーザーにメールで送信するので、ユーザーは貴社のアプリを訪れることなく連携ページ
に到達できます。より長い [`expiresIn`](#jwt-expires-in) と組み合わせてください — デフォルトの 5 分は
受信トレイの中ではまず持ちません。

### Connect Accounts JSON

**`email` 内のすべてのフィールドが必須です。** 1 つでも欠けると送信は失敗します。

```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` は **トップレベル** のパラメータであり、`email` オブジェクトの一部ではありません。
  `email` の中に入れると無視され、ユーザーには 5 分で期限切れになるリンクが届きます。
</Warning>

レスポンスは結果を `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"
}
```

送信の**失敗**は `emailSent: false` として返されるのではなく、代わりに `code: 333` が返されます。
つまり `false` は、メールが要求されなかったことを意味します。

### Connect Accounts メールの例

ソーシャル連携ページを開くメールの例です:

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

メールは以下のアドレスから送信されます:

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

<h2 id="mobile-jwt">
  モバイルアプリ
</h2>

リンク用 URL は、埋め込み webview ではなく必ず**システムブラウザ**で開いてください: webview 内の
サインインは Google が `disallowed_useragent` で拒否し、Meta は完全にブロックします。ユーザーには
ネットワーク自身のエラーページが表示され、貴社側の対処では解決できません。

* **iOS** — `ASWebAuthenticationSession`、または `SFSafariViewController`。
* **Android** — Chrome Custom Tabs。

ネイティブアプリにはイベントを post する先のブラウザウィンドウがないため、結果は代わりに
[Link Session の取得](/docs/apis/profiles/get-link-session)から取得してください。`origin` にカスタム
スキーム(`myapp://connected`)を設定すると、ページからアプリに戻る手段が確保されます。

<h3 id="mobile-code-examples">
  モバイルコード例
</h3>

`linkingURL` を [Link Session の作成](/docs/apis/profiles/create-link-session)が返す `url` に
置き換えてください。

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

## テスト

まず [Postman](/docs/testing/postman) でリンクの作成をテストすることを**推奨します**。ダッシュボードの
Primary Profile の API Key ページから入手できる Integration Package には、サンプルの Postman 設定が
含まれています。インポートし、`profileKey` の *body* フィールドに Profile Key を入力して、*Send* を
クリックしてください。

サンプル設定には現在も `privateKey` と `domain` が事前入力されています。`privateKey` は無視され、
アカウントにリンク用ドメインが複数ない限り `domain` は空にして構いません。

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

## レガシー: generateJWT

<Info>
  [リンク用 URL の生成](/docs/apis/profiles/generate-jwt)(`generateJWT`)は同じ処理を行い、**非推奨**です —
  完全にサポートされ、削除予定はなく、すでに配布済みのリンクについても変更はありません。パラメータの
  詳細は同エンドポイントのページに記載されており、現在は受け付けられて無視される 3 つのパラメータも
  そこで説明しています。

  両方のエンドポイントは同じバリデータを使うため、このページの内容はどちらにも当てはまります。移行時に
  知っておく価値のある違いは 1 つだけです: `generateJWT` は、Link Session の作成が拒否する 3 つのもの —
  未知の `allowedSocial` ネットワーク、X 認証情報の片方のみ、文字列でない `redirect` — を許容します。
</Info>
