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

# Події завершення зв'язування

> Дізнавайтеся в момент, коли ваш користувач підключає акаунт, замість того щоб опитувати.

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

Коли ви відкриваєте сторінку зв'язування у спливаючому вікні, ваша власна сторінка може слухати,
що в ньому відбувається: ваш користувач підключив Reddit, він відмовився, підключення зазнало
невдачі. Ви отримуєте подію на кожен результат, тож ваш UI оновлюється в момент події, а не за
таймером.

Задайте [`origin`](/docs/apis/profiles/create-link-session) під час створення посилання, відкрийте
повернений `url` у спливаючому вікні й слухайте. Більше нічого не потрібно, і нічого не змінюється
для посилань, створених без `origin`, — вони поводяться точно так, як завжди.

<Note>
  **Використовуєте [вбудований віджет](/docs/multiple-users/connect-widget)? Це вже налаштовано за
  вас.** Скрипт отримує ці події та передає їх у `connect.on("success", …)` з відкинутим префіксом
  `connect:` — без слухача `message`, без перевірки origin, без опитування `popup.closed`, які
  довелося б писати. Ця сторінка — дротовий протокол під усім цим і те, з чим ви працюєте, коли
  вікном володієте **ви**: прямий режим або спливаюче вікно, яке ви відкриваєте самостійно.
</Note>

<Note>
  Події надсилаються лише на точний `origin`, який ви задали на посиланні, і лише вікну, яке
  відкрило спливаюче вікно. Все одно завжди перевіряйте `event.origin` у своєму слухачі: будь-яка
  сторінка може надіслати повідомлення вашому вікну, а origin — єдина частина повідомлення, яку не
  можна підробити.
</Note>

## Події

Кожне повідомлення, надіслане вашій сторінці, — це об'єкт у формі
`{ source: "ayrshare", version: 1, event, ... }`, і воно несе `network` щоразу, коли подія про
якусь мережу. Дві події — ні: `connect:closed` із хостованої сторінки зв'язування, де воно
означає, що ваш користувач натиснув Done, а не що завершилося одне підключення, і `connect:ready`
віджета (див. нижче). Результати, які синтезує ваш власний код — заблоковане спливаюче вікно або
вікно, закрите вашим користувачем, — надходять не від нас і несуть лише те, що ви їм дасте.

<Note>
  [Вбудований віджет](/docs/multiple-users/connect-widget) відрізняється однією подією. Його `ready`
  оголошує, що слот піднявся, а не щось про мережу, тож він не несе `network` — тоді як `ready`
  спливаючого вікна називає мережу, для якої його відкрили. Якщо ви опрацьовуєте обидві поверхні
  одним слухачем, читайте `network` на `ready` обережно.

  Віджет також додає одну причину `cancelled`, яку ця поверхня ніколи не надсилає: `superseded`,
  коли другий виклик `popup()` замінює спробу, що ще триває. Тут одне вікно й один результат, тож
  нема чого заміняти.
</Note>

| Подія | Додаткові поля | Надсилається, коли |
| - | - | - |
| `connect:ready` | | Сторінка завантажилася, і посилання перевірено. |
| `connect:started` | | Вашого користувача відправлено до соціальної мережі. |
| `connect:selection` | `step` | На екрані пікер або форма — вашому користувачу є що робити. |
| `connect:success` | `refId`, `displayName` | Акаунт підключено **і збережено**. `refId` — User Profile, до якого його підключено. `displayName` — назва акаунта; пропускається, коли її в нас ще немає. |
| `connect:error` | `message` і `code`, коли він є | Підключення зазнало невдачі. `code` відповідає [довіднику помилок](/docs/errors/overview); він відсутній, коли невдача не має каталогізованого коду, і на будь-якому результаті, який синтезує ваш власний фрагмент, як-от заблоковане спливаюче вікно. |
| `connect:cancelled` | `reason` | Ваш користувач відмовився. `reason` — `popupClosed`, `scopesDenied` або `userCancelled`. |
| `connect:closed` | | Спливаюче вікно ось-ось закриється самостійно. Не несе `network`, коли надходить із хостованої сторінки зв'язування, де воно означає, що ваш користувач натиснув Done, а не що завершилося одне підключення. |

Рівно одна з подій `connect:success`, `connect:error` або `connect:cancelled` надходить на кожне
підключення. `connect:closed` не є однією з них — вона йде слідом, щоб сказати вам, що спливаюче
вікно закрилося навмисно.

`connect:success` надсилається лише після збереження акаунта, тож
[`GET /user`](/docs/apis/user/profile-details) одразу після неї вже показує підключений акаунт.

<Note>
  Спливаюче вікно залишається відкритим після `connect:error`, щоб ваш користувач міг прочитати,
  що пішло не так. Воно закривається самостійно після `connect:success` і `connect:cancelled`.
  Додайте `&autoClose=false` до URL, щоб тримати його відкритим у кожному випадку, поки ви
  налагоджуєте.
</Note>

## Прослуховування

Дві речі, які робить цей фрагмент і які легко пропустити. Він перевіряє `event.origin` і стежить
за спливаючим вікном, закритим вашим користувачем вручну — закрите вікно не може нічого надіслати,
тож опитування — єдиний спосіб це помітити.

```javascript theme={"system"}
function connectAccount(url) {
  // Derive the origin from the URL you were given rather than hard-coding one:
  // if your account uses its own linking domain, the popup runs on that.
  const popupOrigin = new URL(url).origin;

  const popup = window.open(url, "ayrshare-connect", "width=600,height=800");
  if (!popup) {
    handleOutcome({ event: "connect:error", message: "The popup was blocked." });
    return;
  }

  // Declared before anything can call `cleanup`: a message arriving early would
  // otherwise hit `poll` in its temporal dead zone and throw.
  let poll;

  const cleanup = () => {
    clearInterval(poll);
    window.removeEventListener("message", onMessage);
  };

  const onMessage = event => {
    // Both checks, not just the origin: another tab or frame on the same origin
    // could otherwise post a message your handler would believe.
    if (event.origin !== popupOrigin || event.source !== popup) return;
    const message = event.data;
    if (!message || message.source !== "ayrshare") return;

    if (["connect:success", "connect:error", "connect:cancelled"].includes(message.event)) {
      // `finally`, so your own handler throwing cannot leave the poll running —
      // it would later see the closed popup and report `cancelled` on top of the
      // outcome you already had. Cleanup also matters after `connect:error`,
      // where the popup stays open so your user can read it.
      try {
        handleOutcome(message);
      } finally {
        cleanup();
      }
    }
  };
  window.addEventListener("message", onMessage);

  // A hand-closed popup sends nothing, so watch for it. Wait a moment before
  // deciding: the popup closes itself right after sending, and the message can
  // still be in flight when you first see the window go.
  let closedAt = null;
  poll = setInterval(() => {
    if (!popup.closed) return;
    if (closedAt === null) {
      closedAt = Date.now();
      return;
    }
    if (Date.now() - closedAt < 750) return;

    cleanup();
    handleOutcome({ event: "connect:cancelled", reason: "popupClosed" });
  }, 500);
}
```

## Помилки

`connect:error` несе ті самі коди, що й решта API, тож код, який ви бачите тут, означає те саме,
що й будь-де інде. Єдиний специфічний для цієї поверхні:

| Код | Значення |
| - | - |
| `513` | Посилання відкрили як вікно підключення однієї мережі, але створене воно було не для цього. Досяжний, лише якщо ви будуєте такий URL самостійно — `url`, який повертає цей ендпоінт, завжди відповідає створеному посиланню. |

Усе інше — власна невдача соціальної мережі, повідомлена з тим кодом, який ця невдача вже має —
наприклад, `322` для проблеми авторизації Instagram, що включає акаунт, який усе ще Personal, а не
Professional.

### Мертве посилання надходить інакше

Посилання, яке **прострочене**, **відкликане** або **невідоме**, відхиляється до того, як сторінка
встигне дізнатися, куди надсилати події, тож надіслати їх воно не може. Ваш користувач бачить
причину та її код на екрані, а ваша сторінка чує `connect:cancelled`, коли він закриває вікно.

Щоб їх розрізнити, опитуйте [Отримання Link Session](/docs/apis/profiles/get-link-session): воно
авторитетно повідомляє `expired` і `revoked` та повертає `502` для посилання, якого не існує.

## Якщо ви радше не керуватимете спливаючим вікном

Два варіанти, і лише другий відмовляється від подій.

**Нехай вікном володіє віджет.** [Вбудований віджет](/docs/multiple-users/connect-widget) відкриває
його й стежить за ним за вас і доставляє кожну з наведених вище подій вашим обробникам. Ви все
одно отримуєте `success` у момент збереження акаунта; ви просто не пишете сантехніку. Його фрейми
також означають, що більшість мереж узагалі не відкривають спливаючого вікна до власного входу
мережі.

**Опитуйте натомість.** Якщо спливаюче вікно справді недоступне — застосунок із серверним
рендерингом або мобільний застосунок, що відкриває посилання в системному браузері, — опитуйте
[Отримання Link Session](/docs/apis/profiles/get-link-session). Воно повідомляє `completedAt` і
`completedNetworks`, щойно акаунт збережено — це той самий момент, коли було б надіслано
`connect:success`. Telegram завжди завершується саме так, оскільки він завершує підключення поза
основним потоком, без жодного браузерного колбека.


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