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

# Tạo một Link Session

> Tạo URL liên kết mạng xã hội cho một user profile, mà không cần gửi private key.

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

export const HeaderAPI = ({noProfileKey, profileKeyRequired}) => <>
    <ParamField header="Authorization" type="string" required>
      <a href="/docs/apis/overview#authorization">API Key</a> of the Primary Profile.
      <br />
      <br />
      Format: <code>Authorization: Bearer API_KEY</code>
    </ParamField>
    {!noProfileKey && (profileKeyRequired ? <ParamField header="Profile-Key" type="string" required>
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField> : <ParamField header="Profile-Key" type="string">
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField>)}
  </>;

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

Tạo URL liên kết mạng xã hội cho một User Profile. Gửi `url` được trả về cho người dùng của bạn,
và họ mở nó để kết nối các tài khoản mạng xã hội của mình.

Đây là cách được khuyến nghị để tạo URL liên kết. Nó chỉ cần API key của bạn và một
`Profile-Key` — không có private key nào phải gửi và không có gì phải ký. Khác với URL liên kết
được tạo trước đây, một link session được lưu lại, nên bạn có thể kiểm tra xem nó đã được dùng
chưa và thu hồi nó trước khi hết hạn.

`url` được trả về sẽ đăng nhập người dùng của bạn vào profile của họ, nên hãy coi nó như một
mật khẩu và chỉ gửi mỗi URL cho một người dùng duy nhất. Xem
[Gửi URL liên kết](/docs/apis/profiles/social-linking-overview#sending-the-linking-url).

<Note>
  URL có hiệu lực trong **5 phút** theo mặc định. Dùng `expiresIn` để đặt một khoảng
  thời gian khác, tối đa 2880 phút (48 giờ).
</Note>

<Info>
  [Tạo URL liên kết](/docs/apis/profiles/generate-jwt) thực hiện cùng một thao tác và vẫn hoạt
  động không thay đổi. Nó chấp nhận các tham số cũ `privateKey`, `base64` và `verify`
  và bỏ qua chúng. `domain` không bị bỏ qua trên cả hai endpoint — nó vẫn không bắt buộc và
  vẫn được kiểm tra. Các tích hợp mới nên dùng endpoint này.

  Một khác biệt trong phản hồi: `generateJWT` trả về một `token` ở cấp cao nhất để tương thích
  ngược, còn endpoint này không trả về token **bên cạnh** một `url` — token trong một `url` nằm
  bên trong nó. Nếu bạn đang di chuyển và mã của bạn đọc `token`, hãy đọc `url` thay thế.
  ([Chế độ connect](#connect-mode) cho widget nhúng là hình dạng duy nhất trả về một `token`
  trần, vì nó không trả về URL nào để token nằm trong đó.)
</Info>

## Tham số Header

<HeaderAPI profileKeyRequired={true} />

<Note>
  `Profile-Key` là một header trên endpoint này — không có tham số body `profileKey`.
  Nếu thiếu nó bạn nhận được `code: 188`, mà thông báo của nó liệt kê `privateKey`,
  `profileKey` và các tên trường cũ khác vì nó được dùng chung với
  [Tạo URL liên kết](/docs/apis/profiles/generate-jwt). Hãy hiểu nó là "header Profile-Key bị
  thiếu hoặc sai"; không có tên nào khác trong đó là tham số của endpoint này.
</Note>

<ParamField header="X-Twitter-OAuth1-Api-Key" type="string">
  X API Key (Consumer Key) của bạn từ X Developer Portal. Khi được cung cấp, URL liên kết
  sẽ sử dụng X Developer App của riêng bạn cho liên kết OAuth.
</ParamField>

<ParamField header="X-Twitter-OAuth1-Api-Secret" type="string">
  X API Secret (Consumer Secret) của bạn từ X Developer Portal. Bắt buộc khi
  `X-Twitter-OAuth1-Api-Key` được cung cấp.
</ParamField>

<h2 id="body-parameters">
  Tham số Body
</h2>

<ParamField body="mode" type="string" default="grid">
  Phiên này vận hành bề mặt liên kết nào.

  * `grid` — trang liên kết được lưu trữ, hiển thị mọi mạng bạn cho phép. Đây là mặc định, nên một
    yêu cầu bỏ qua `mode` sẽ tạo loại này.
  * `connect` — từng mạng một, được mở từ dashboard của riêng bạn. Xem
    [Chế độ Connect](#connect-mode) bên dưới và [Chế độ trực tiếp](/docs/multiple-users/connect-direct-mode).

  Không bao giờ được suy luận: truyền `origin` hoặc `network` không đưa bạn vào chế độ connect,
  nên một phiên grid không thể vô tình trở thành một phiên bị kiểm soát. Bất kỳ giá trị nào khác
  trả về `code: 188` với `details` nêu tên hai giá trị hợp lệ.
</ParamField>

<ParamField body="expiresIn" type="number" default={5}>
  Thời hạn của liên kết tính bằng phút. Phạm vi: 1 đến 2880 phút.

  Yêu cầu Max Pack.

  Xem [Thời hạn Liên kết](/docs/apis/profiles/social-linking-overview#jwt-expires-in) để biết thêm thông tin.
</ParamField>

<ParamField body="logout" type="boolean" default={false}>
  Tự động đăng xuất phiên hiện tại. Không khuyến nghị trong production, vì nó
  ảnh hưởng đến hiệu suất.

  Xem [Tự động đăng xuất phiên Profile](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session).
</ParamField>

<ParamField body="redirect" type="string">
  Một URL để chuyển hướng đến khi nút "Done" (Hoàn tất) hoặc hình ảnh logo được nhấp. Thêm
  tham số truy vấn `origin=true` để chuyển hướng cửa sổ opener.
</ParamField>

<ParamField body="allowedSocial" type="array">
  Các mạng xã hội hiển thị trên trang liên kết. Ghi đè các mạng được cấu hình
  trong trang [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access).

  ```json Only display Facebook, X/Twitter, LinkedIn, and TikTok theme={"system"}
  {
    "allowedSocial": ["facebook", "twitter", "linkedin", "tiktok"]
  }
  ```
</ParamField>

<ParamField body="network" type="string">
  Chỉ dành cho chế độ connect. Mạng xã hội duy nhất mà phiên này kết nối, chính là điều biến nó
  thành một phiên **chế độ trực tiếp**. Bỏ qua nó cho một phiên mà dashboard của riêng bạn vận
  hành trên nhiều mạng.

  Một trong `bluesky`, `facebook`, `gmb`, `instagram`, `instagramApi`, `linkedin`, `pinterest`,
  `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, `whatsapp`, `x`, `youtube`.
  Bất kỳ giá trị nào khác trả về `code: 508` — bao gồm cả `fbg`, vốn không phải là một đích liên
  kết ở đây.

  Không thể kết hợp với `allowedSocial` (`code: 507`): một phiên cho một mạng duy nhất tự nó đã là
  allowlist của chính nó. Một mạng mà tài khoản của bạn chưa bật trả về `code: 509`, đây là một
  câu trả lời khác với 508 vì nó có thể khắc phục được trên trang
  [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) của bạn.

  Trong chế độ grid nó bị bỏ qua.
</ParamField>

<ParamField body="instagramLinkMethod" type="string">
  Ghi đè luồng liên kết Instagram nào được sử dụng cho liên kết này. Các giá trị hợp lệ:

  * `instagram`: Instagram Login trực tiếp, không yêu cầu Facebook Page.
  * `facebook`: Liên kết Instagram thông qua Facebook Page đã kết nối.

  Khi bị bỏ qua, trang liên kết sử dụng cài đặt
  [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) toàn tài khoản của bạn.
</ParamField>

<ParamField body="origin" type="string">
  Origin chính xác của trang đã mở cửa sổ liên kết, để nó có thể được thông báo khi việc
  liên kết hoàn tất.

  Khi được đặt, trang liên kết gửi các sự kiện đến origin đó bằng `window.postMessage` khi
  người dùng của bạn kết nối từng tài khoản, và trang của bạn có thể phản ứng mà không cần
  polling. Các sự kiện chỉ được gửi đến chính xác giá trị này, nên nó phải khớp origin của trang
  bạn từng ký tự, bao gồm cả scheme và cổng nếu có.

  Ba hình dạng được chấp nhận: một origin `https` (`https://app.example.com`),
  `http://localhost:3000` cho phát triển cục bộ, và một custom scheme gốc
  (`myapp://connected`). Bất cứ thứ gì khác — một origin `http://` thuần không phải localhost,
  hoặc thứ gì đó hoàn toàn không phải origin — sẽ bị bỏ qua trên các liên kết mà endpoint này
  tạo: liên kết vẫn hoạt động, nó chỉ đơn giản không gửi sự kiện nào. Nó là không bắt buộc, nên
  bỏ qua nó cũng không phải là lỗi.

  Trong ba hình dạng, chỉ hai hình dạng đầu nhận được sự kiện. Một custom scheme là một đích
  quay lại cho ứng dụng mobile và **không thể nhận chúng**, vì không có cửa sổ trình duyệt nào
  để gửi tới; ứng dụng gốc poll [Lấy một Link Session](/docs/apis/profiles/get-link-session) thay thế.

  Xem [Sự kiện Hoàn tất Liên kết](/docs/multiple-users/link-completion-events).

  **Trong chế độ connect, `origin` là bắt buộc, và nó được kiểm tra.** Sự khoan dung ở trên là
  hành vi của chế độ grid. Với `mode: "connect"`, bỏ qua nó trả về `code: 505` và một giá trị
  không thuộc một trong ba hình dạng được chấp nhận trả về `code: 506`, mà `details` của nó lặp
  lại hình dạng bạn đã gửi.
</ParamField>

<ParamField body="domain" type="string">
  Không bắt buộc. Domain liên kết của bạn, khi tài khoản của bạn có nhiều hơn một. Nếu bỏ
  trống, domain của chính tài khoản bạn sẽ được dùng. Một domain chưa đăng ký cho tài khoản
  của bạn sẽ bị từ chối.
</ParamField>

<ParamField body="email" type="object">
  Gửi email Connect Accounts mang theo liên kết, để người dùng của bạn có thể truy cập
  trực tiếp trang liên kết của họ. Yêu cầu một địa chỉ `to`.

  Yêu cầu Max Pack. Phản hồi báo cáo kết quả trong `emailSent`. Nếu không gửi được email, bạn vẫn nhận được liên kết: phản hồi có `emailSent: false` và một đối tượng `emailError`, để bạn có thể gửi liên kết cho người dùng theo cách khác.

  Xem [Connect Accounts Email](/docs/apis/profiles/social-linking-overview#connect-accounts-email).
</ParamField>

<h2 id="connect-mode">
  Chế độ Connect
</h2>

`mode: "connect"` tạo một phiên cho một bề mặt liên kết mà bạn tự lưu trữ, thay vì cho trang liên
kết được lưu trữ. Bạn nhận được hình dạng connect nào trong hai hình dạng phụ thuộc vào một điều —
bạn có truyền `network` hay không:

| Bạn gửi | Bạn nhận lại | Bạn làm gì với nó |
| - | - | - |
| `mode: "connect"` và một `network` | một `url` trỏ đến một trang connect cho một mạng duy nhất | mở nó trong một cửa sổ bật lên — đây là [chế độ trực tiếp](/docs/multiple-users/connect-direct-mode) |
| `mode: "connect"` và không có `network` | một `token`, và hoàn toàn không có URL | giao nó cho front end của riêng bạn |

<Note>
  **Phản hồi mang bí mật đúng một lần.** Một phản hồi có hoặc một `url` hoặc một `token`, không
  bao giờ cả hai và không bao giờ hai URL. Token trong một phiên chế độ trực tiếp nằm bên trong
  `url`, giống như trong chế độ grid; một phiên không có URL để mang nó sẽ trả về `token` trần
  thay thế. Mọi thứ khác đều giống nhau trong cả ba chế độ: `sessionId`, `expiresAt`, `emailSent`,
  và `title` khi User Profile có một tiêu đề.
</Note>

### Chế độ Connect Yêu cầu Gì

Không cái nào trong hai điều này là một trường body riêng — cái đầu là một quyền của tài khoản và
cái thứ hai là tham số [`origin`](#body-parameters) ở trên, thứ mà chế độ connect biến thành bắt
buộc.

**[Max Pack](/docs/additional/maxpack).** Không có nó, lệnh gọi trả về `code: 504`, được kiểm tra trước
các tham số chế độ connect, nên việc sửa `origin` hay `network` sẽ không thay đổi câu trả lời. Hãy
liên hệ bộ phận hỗ trợ nếu bạn cần bật chế độ connect trên một tài khoản không có Max Pack.

**Một `origin`, trên mỗi phiên.** Không có allowlist và không có bước đăng ký — bạn gửi nó ở mỗi
lệnh gọi và nó được lưu trên phiên, nên một môi trường mới không cần thiết lập gì ở phía chúng
tôi. Ba hình dạng được chấp nhận:

* một origin `https` — `https://app.example.com`
* một custom scheme gốc — `myapp://connected`
* `http://localhost` hoặc `http://localhost:3000`, cho phát triển cục bộ

Chỉ origin: không có path, query hay fragment, và không có thông tin đăng nhập trong đó. Bỏ qua nó
trả về `code: 505`, và bất cứ thứ gì không thuộc một trong ba hình dạng trả về `code: 506`.

<Warning>
  `email` không thể dùng với một phiên không có `network`, vì không có liên kết nào để đặt vào
  email — hình dạng đó trả về một token cho front end của riêng bạn. Lệnh gọi trả về `code: 510`.
  Hãy thêm một `network` cho một phiên chế độ trực tiếp, vốn có URL, hoặc bỏ `email`.
</Warning>

Mọi mã được nêu ở trên đều có trong tài liệu tham khảo
[Lỗi Link Session](/docs/errors/errors-ayrshare#link-session-errors).

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

  ```javascript JavaScript theme={"system"}
  const API_KEY = "API_KEY";
  const PROFILE_KEY = "PROFILE_KEY";

  fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
      "Profile-Key": PROFILE_KEY,
    },
    body: JSON.stringify({ expiresIn: 60 }),
  })
    .then((res) => res.json())
    .then((json) => console.log(json))
    .catch(console.error);
  ```

  ```python Python theme={"system"}
  import requests

  payload = {'expiresIn': 60}
  headers = {'Content-Type': 'application/json',
          'Authorization': 'Bearer API_KEY',
          'Profile-Key': 'PROFILE_KEY'}

  response = requests.post('https://api.ayrshare.com/api/profiles/link-sessions',
                           json=payload, headers=headers)
  print(response.json())
  ```

  ```php PHP theme={"system"}
  <?php
  require 'vendor/autoload.php';    // Composer auto-loader using Guzzle. See .../guzzlephp.org/en/stable/overview.html

  $client = new GuzzleHttp\Client();
  $res = $client->request(
      'POST',
      'https://api.ayrshare.com/api/profiles/link-sessions',
      [
          'headers' => [
              'Content-Type'  => 'application/json',
              'Authorization' => 'Bearer API_KEY',
              'Profile-Key'   => 'PROFILE_KEY'
          ],
          'json' => [
              'expiresIn' => 60,
          ]
      ]
  );

  echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT);
  ```

  ```csharp C# theme={"system"}
  using System;
  using System.Net.Http;
  using System.Text;
  using System.Threading.Tasks;
  using Newtonsoft.Json;

  namespace CreateLinkSession_csharp
  {
    class CreateLinkSession
    {
        static async Task Main(string[] args)
        {
            string API_KEY = "API_KEY";
            string PROFILE_KEY = "PROFILE_KEY";
            string url = "https://api.ayrshare.com/api/profiles/link-sessions";

            try
            {
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY);
                    client.DefaultRequestHeaders.Add("Profile-Key", PROFILE_KEY);

                    var sendData = new { expiresIn = 60 };
                    string jsonData = JsonConvert.SerializeObject(sendData);
                    var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

                    HttpResponseMessage response = await client.PostAsync(url, content);
                    response.EnsureSuccessStatusCode();

                    string responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(responseBody);
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"HTTP request error: {e.Message}");
            }
        }
    }
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",  // Identifier for this link. Use it with Get and Revoke a Link Session.
      "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN",  // Send this to your user exactly as returned. The token exists only in here.
      "expiresAt": "2026-09-02T08:03:26.838Z",  // When the link stops working, as an ISO 8601 timestamp.
      "emailSent": false,  // Whether the connect-accounts email was sent. false with no emailError means no email was requested.
      "title": "Acme Client"  // The User Profile's title. Omitted when the profile has none.
  }
  ```

  ```json 200: Email Failed to Send theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN",  // Still valid. Send it to your user yourself.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,
      "emailError": {  // Only present when an email was requested and could not be sent.
          "code": 333,
          "message": "Email failed to send. Please confirm a valid email was provided."
      },
      "title": "Acme Client"
  }
  ```

  ```json 200: Direct Mode (mode: "connect" with a network) theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "url": "https://profile.ayrshare.com/connect?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu",  // Open this in a popup. One network, no domain parameter.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,
      "title": "Acme Client"
  }
  ```

  ```json 200: Connect Mode Without a Network theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "token": "ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu",  // No url: this shape returns the bare token instead. Treat it like a password.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,  // Always false here - email needs a link to send, so it returns code: 510.
      "title": "Acme Client"
  }
  ```

  ```json 401: Connect Mode Requires the Max Pack theme={"system"}
  {
    "action": "link session",
    "status": "error",
    "code": 504,
    "message": "Connect mode requires the Max Pack. Activate it on your Account page: https://app.ayrshare.com/account"
  }
  ```

  ```json 400: Missing Profile-Key Header theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 188,
    "message": "Missing or incorrect privateKey, profileKey, domain, email 'to', or expiresIn fields."
  }
  ```

  ```json 400: Domain Not Registered to Your Account theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 189,
    "message": "Error generating JWT. Check the sent parameters.",
    "details": "Missing or incorrect domain."
  }
  ```

  ```json 403: expiresIn Requires the Max Pack theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 340,
    "message": "Max Pack required. Go to your dashboard to add the Max Pack."
  }
  ```
</ResponseExample>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.