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

# Eventos de finalización de vinculación

> Entérate cuando tu usuario conecta una cuenta, en lugar de hacer polling para saberlo.

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

Cuando abres una página de vinculación en una ventana emergente, tu propia página puede escuchar lo
que ocurre en ella: tu usuario conectó Reddit, se echó atrás, la conexión falló. Recibes un evento
por resultado, así que tu UI se actualiza en el momento en que sucede en lugar de con un
temporizador.

Establece [`origin`](/docs/apis/profiles/create-link-session) al crear el enlace, abre la `url` devuelta
en una ventana emergente y escucha. No se requiere nada más, y nada cambia para los enlaces creados
sin `origin` — se comportan exactamente como siempre.

<Note>
  **¿Usas el [widget incrustado](/docs/multiple-users/connect-widget)? Esto ya está cableado por ti.**
  El script recibe estos eventos y los entrega a `connect.on("success", …)` con el prefijo
  `connect:` eliminado — sin listener de `message`, sin comprobación de origen, sin polling de
  `popup.closed` que escribir. Esta página es el protocolo de cable subyacente, y aquello contra lo
  que construyes cuando la ventana es **tuya**: el modo directo, o una ventana emergente que abres tú
  mismo.
</Note>

<Note>
  Los eventos se envían solo al `origin` exacto que estableciste en el enlace, y solo a la ventana
  que abrió la ventana emergente. Comprueba `event.origin` en tu listener de todos modos: cualquier
  página puede enviar un mensaje a tu ventana, y el origen es la única parte de un mensaje que no se
  puede falsificar.
</Note>

## Los eventos

Cada mensaje enviado a tu página es un objeto con la forma
`{ source: "ayrshare", version: 1, event, ... }`, y lleva `network` siempre que el evento trate de
una red. Dos eventos no lo hacen: un `connect:closed` procedente de la página de vinculación
alojada, donde significa que tu usuario pulsó Done y no que una conexión terminó, y el
`connect:ready` del widget (mira abajo). Los resultados que sintetiza tu propio código — una ventana
emergente bloqueada, o una ventana que tu usuario cerró — no proceden de nosotros y llevan solo lo
que tú les des.

<Note>
  El [widget incrustado](/docs/multiple-users/connect-widget) difiere en un evento. Su `ready` anuncia
  que un slot está listo y no nada sobre una red, así que no lleva `network` — mientras que el
  `ready` de la ventana emergente nombra la red para la que se abrió. Si gestionas ambas superficies
  desde un solo listener, lee `network` a la defensiva en `ready`.

  El widget también añade una razón de `cancelled` que esta superficie nunca envía: `superseded`,
  cuando una segunda llamada a `popup()` reemplaza un intento aún en curso. Aquí hay una ventana y un
  resultado, así que no hay nada que reemplazar.
</Note>

| Evento | Campos extra | Se envía cuando |
| - | - | - |
| `connect:ready` | | La página se ha cargado y el enlace se ha comprobado. |
| `connect:started` | | Tu usuario ha sido enviado a la red social. |
| `connect:selection` | `step` | Hay un selector o un formulario en pantalla — tu usuario tiene algo que hacer. |
| `connect:success` | `refId`, `displayName` | La cuenta está conectada **y guardada**. `refId` es el User Profile al que se conectó. `displayName` es el nombre de la cuenta, y se omite cuando todavía no tenemos uno. |
| `connect:error` | `message`, y `code` cuando lo hay | La conexión falló. `code` coincide con la [referencia de errores](/docs/errors/overview); está ausente cuando el fallo no tiene código catalogado, y en cualquier resultado que sintetice tu propio snippet, como una ventana emergente bloqueada. |
| `connect:cancelled` | `reason` | Tu usuario se echó atrás. `reason` es `popupClosed`, `scopesDenied` o `userCancelled`. |
| `connect:closed` | | La ventana emergente está a punto de cerrarse sola. No lleva `network` cuando procede de la página de vinculación alojada, donde significa que tu usuario pulsó Done y no que una conexión terminó. |

Exactamente uno de `connect:success`, `connect:error` o `connect:cancelled` llega por conexión.
`connect:closed` no es uno de ellos — llega después, para decirte que la ventana emergente se cerró
a propósito.

`connect:success` se envía solo después de que la cuenta se haya guardado, así que un
[`GET /user`](/docs/apis/user/profile-details) inmediatamente después ya muestra la cuenta conectada.

<Note>
  La ventana emergente permanece abierta tras `connect:error` para que tu usuario pueda leer qué
  salió mal. Se cierra sola tras `connect:success` y `connect:cancelled`. Añade `&autoClose=false`
  a la URL para mantenerla abierta en todos los casos mientras depuras.
</Note>

## Escuchar

Dos cosas que hace este snippet y que son fáciles de omitir. Comprueba `event.origin`, y vigila una
ventana emergente que tu usuario cerró a mano — una ventana cerrada no puede enviar nada, así que el
polling es la única forma de notarlo.

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

## Errores

`connect:error` lleva los mismos códigos que el resto de la API, así que un código que veas aquí
significa lo mismo que en cualquier otro sitio. El específico de esta superficie:

| Código | Significado |
| - | - |
| `513` | El enlace se abrió como una ventana de conexión de una sola red, pero no se creó para una. Solo alcanzable si construyes esa URL tú mismo — la `url` que devuelve este endpoint siempre coincide con el enlace que creó. |

Cualquier otra cosa es un fallo de la propia red social, reportado con el código que ese fallo ya
tenga — por ejemplo `322` para un problema de autorización de Instagram, lo que incluye una cuenta
que sigue siendo Personal en lugar de Professional.

### Un enlace muerto llega de otra manera

Un enlace **caducado**, **revocado** o **desconocido** se rechaza antes de que la página pueda saber
adónde enviar eventos, así que no puede enviar ninguno. Tu usuario ve el motivo y su código en
pantalla, y tu página oye `connect:cancelled` cuando cierra la ventana.

Para distinguirlos, haz polling de [Obtener una Link Session](/docs/apis/profiles/get-link-session):
informa de `expired` y `revoked` con autoridad, y devuelve `502` para un enlace que no existe.

## Si prefieres no gestionar una ventana emergente

Dos opciones, y solo la segunda renuncia a los eventos.

**Deja que el widget sea el dueño de la ventana.** El
[widget incrustado](/docs/multiple-users/connect-widget) la abre y la supervisa por ti y entrega todos los
eventos de arriba a tus handlers. Sigues recibiendo `success` en el momento en que una cuenta se
guarda; simplemente no escribes la fontanería. Sus frames además hacen que la mayoría de las redes no
abran ninguna ventana emergente hasta el login de la propia red.

**Haz polling en su lugar.** Si una ventana emergente no está genuinamente disponible — una app
renderizada en servidor, o una app móvil que abre el enlace en el navegador del sistema — haz polling
de [Obtener una Link Session](/docs/apis/profiles/get-link-session). Informa de `completedAt` y
`completedNetworks` en cuanto una cuenta se guarda, que es el mismo momento en que se habría enviado
`connect:success`. Telegram siempre termina de esta manera, ya que completa fuera de banda sin ningún
callback de navegador.


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