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

# Ayrshare Connect Widget

> Lassen Sie Ihre Nutzer Social-Media-Konten direkt aus Ihrem eigenen Dashboard verknüpfen — mit einem Script-Tag und unseren in Ihre Seite eingebetteten Schaltflächen.

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={true} />

Das Ayrshare Connect Widget bringt unsere Verknüpfungs-Schaltflächen **in Ihr eigenes Dashboard**.
Sie laden ein Skript, platzieren einen Slot dort, wo ein Netzwerk in Ihr Layout gehört, und wir
rendern dort eine Schaltfläche, die bereits anzeigt, ob das Konto verbunden ist. Ihr Nutzer klickt
darauf und verknüpft das Konto, ohne Ihre Seite zu verlassen — höchstens ein Popup, das des
Netzwerks selbst.

Sie schreiben keine Popup-Behandlung, keine OAuth-Callbacks, keinen Session-Refresh und keine Logik
pro Netzwerk. Wenn ein Netzwerk auf seiner Seite etwas ändert, wird die Korrektur in unseren Frames
ausgeliefert, sobald wir sie deployen; Sie deployen nichts neu.

## Welche Oberfläche Sie möchten

| Oberfläche                                                                                                                     | Ihr Nutzer sieht                                                                         | White-Labeling                                                                           | Wählen Sie sie, wenn                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Widget — eingebettete Frames** ([`mount()`](#mount-a-slot))                                                                  | unsere Schaltflächen inline in Ihrem eigenen Layout; kein Popup bis zu dem des Netzwerks | **Am stärksten.** Ihre Seite, Ihre Schriften und Farben, und Ihr Nutzer verlässt sie nie | Sie haben ein Dashboard mit einer Zeile pro Netzwerk und möchten, dass die Verknüpfung an Ort und Stelle geschieht. Max Pack. |
| **Widget — Ihre eigene Schaltfläche** ([`popup()`](#your-own-button))                                                          | Ihre Schaltfläche, dann ein Popup für das Netzwerk                                       | **Stark.** Das Popup ist unseres, aber es ist kurz und übernimmt Ihr Erscheinungsbild    | Sie möchten Ihre eigene Schaltfläche und Ihr eigenes Styling, und keine Frames in Ihrem Layout. Max Pack.                     |
| **Gehostete Verknüpfungsseite** ([Anleitung](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | eine von uns gehostete Seite mit Ihrem Logo, Ihren Farben und eigenem CSS                | **Am schwächsten.** Es ist unsere Seite, und Ihr Nutzer verlässt Ihre, um sie zu nutzen  | Sie möchten einen Link zum Weitergeben, oder Sie onboarden per E-Mail. Nichts zu bauen, kein Max Pack.                        |

<div className="my-8">
  <Frame caption="Eingebettete Frames: unsere Kacheln, gerendert innerhalb Ihres eigenen Layouts, ein Slot pro Netzwerk oder ein Slot für mehrere.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-frames.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=d6c26228a418421772f753f31b0dfc59" alt="Ein Kunden-Dashboard mit Ayrshare-Connect-Kacheln für Instagram, TikTok und LinkedIn, eingebettet in eine Karte" width="2400" height="1120" data-path="images/multiple-users/connect-widget-frames.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Ihre eigene Schaltfläche: Sie rendern die Schaltfläche, und ein kurzes Popup von uns übernimmt das Netzwerk.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=b68021d477703d97a5994ee85d299be6" alt="Ein Kunden-Dashboard mit eigenen Connect-Schaltflächen und einem Ayrshare-Popup mit dem Facebook-Übergabebildschirm" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="Gehostete Verknüpfungsseite: eine von uns gehostete Seite mit Ihrem Logo und Ihren Farben, für die Ihr Nutzer Ihre App verlässt.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-hosted.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=4d22a992f6e70808fd9ad9f065ed3879" alt="Die gehostete Social-Verknüpfungsseite mit allen verfügbaren Netzwerken" width="2336" height="1906" data-path="images/multiple-users/connect-widget-hosted.webp" />
  </Frame>
</div>

<Note>
  Die beiden Widget-Zeilen sind **eine Integration**, nicht zwei. Ein einziges `init` gibt Ihnen
  beides: Mounten Sie Frames dort, wo Sie unsere Schaltflächen möchten, und rufen Sie überall sonst
  `popup()` aus Ihrer eigenen Schaltfläche auf. Beide teilen sich eine Session und melden sich über
  dieselben Handler.

  [Direct Mode](/docs/multiple-users/connect-direct-mode) ist dasselbe Popup **ohne** unser Skript — für
  eine Seite mit strikter Content-Security-Policy, eine servergerenderte Seite oder eine native App.
  Dort öffnen und überwachen Sie das Popup selbst.
</Note>

## Das Skript einbinden

Pinnen Sie eine Version samt Hash, oder folgen Sie einem Channel ohne Hash. Nie beides — ein
`integrity`-Attribut auf einer beweglichen URL funktioniert ab unserem nächsten Release nicht mehr,
weil sich die Datei dahinter legitim geändert hat.

```html Pinned version theme={"system"}
<script
  src="https://app.ayrshare.com/ayrshare-connect/1.2.0/widget.js"
  integrity="sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz"
  crossorigin="anonymous"
></script>
```

```html Tracking v1 theme={"system"}
<script src="https://app.ayrshare.com/ayrshare-connect/v1/widget.js" crossorigin="anonymous"></script>
```

| Pfad                                    | Caching                  | `integrity`     |
| --------------------------------------- | ------------------------ | --------------- |
| `/ayrshare-connect/<version>/widget.js` | unveränderlich, ein Jahr | **ja** — pinnen |
| `/ayrshare-connect/v1/widget.js`        | fünf Minuten             | nein            |
| `/ayrshare-connect/latest/widget.js`    | fünf Minuten             | nein            |

**`v1` ist der empfohlene Channel.** Er nimmt Korrekturen mit, überschreitet aber nie einen
Breaking Change. `latest` überschreitet per Definition Major-Versionen und liefert Ihrer Seite
daher irgendwann eine Version, deren Verhalten Sie nicht geprüft haben.

Der Hash jeder Version wird in
[`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json) veröffentlicht, die auch
benennt, was jeder Channel aktuell ausliefert:

```json manifest.json theme={"system"}
{
  "versions": {
    "1.2.0": { "integrity": "sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz" }
  },
  "latest": "1.2.0",
  "channels": { "v1": "1.2.0", "latest": "1.2.0" }
}
```

Jedes Bundle beginnt außerdem mit einem Kommentar, der seine eigene Version benennt — der
schnellste Weg, uns mitzuteilen, was eine Seite tatsächlich ausführt:

```js theme={"system"}
/*! ayrshare-connect 1.2.0 */
```

### Content-Security-Policy

Wenn Ihre Seite eine Content-Security-Policy sendet, braucht sie **zwei** Einträge, beide mit dem
Host, von dem Sie das Skript laden:

```
script-src https://app.ayrshare.com;
frame-src  https://app.ayrshare.com;
```

Das ist die vollständige Liste. Sie brauchen **keinen `connect-src`-Eintrag** für uns — unsere
Frames erreichen unsere API aus sich selbst heraus, nicht von Ihrer Seite aus — und **keinen
Eintrag für Popups**, die als Top-Level-Fenster nicht Ihrer Policy unterliegen. Beide Einträge
wurden gemessen: Ohne `script-src` wird das Skript blockiert, ohne `frame-src` der Frame.

## Eine Instanz starten

`session` ist die einzige erforderliche Option. Sie wird **einmal pro Instanz** aufgerufen, nicht
einmal pro Mount, und muss die Antwort Ihres Backends auf
[Eine Link Session erstellen](/docs/apis/profiles/create-link-session) mit `mode: "connect"`
zurückgeben — `{ sessionId, token, expiresAt }` — genau so, wie sie zurückkam.

```javascript Your page theme={"system"}
const connect = AyrshareConnect.init({
  session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
  appearance: { "--ayr-connect-accent": "#0B7A6C" },
  maxHeight: 800,
});
```

```javascript Your backend theme={"system"}
app.get("/my-api/ayrshare-session", async (req, res) => {
  const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`,
      "Profile-Key": profileKeyFor(req.user),
    },
    body: JSON.stringify({ mode: "connect", origin: "https://app.example.com" }),
  });

  res.json(await response.json());
});
```

Eine Widget-Session benennt **kein** `network` — sie autorisiert jedes Netzwerk, das Ihr Account
erlaubt, und welche erscheinen, wird pro Mount entschieden, in Ihrer Seite. Sie trägt aber ein
`origin`, das Einzige, was die Frames überhaupt rendern lässt: Ein Frame prüft die Seite, die ihn
einbettet, gegen diesen Wert und verweigert das Rendern überall sonst.

<Warning>
  Erstellen Sie die Session auf Ihrem Server. Der Aufruf benötigt Ihren API Key, und das
  zurückgegebene Token meldet Ihren Nutzer in seinem User Profile an — behandeln Sie es wie ein
  Passwort.
</Warning>

Wir rufen `session` erneut auf, bevor das Token abläuft, sodass das Widget auf einer den ganzen Tag
geöffneten Seite weiterarbeitet. Ein Aufruf, der fehlschlägt oder kein Token zurückgibt, wird noch
zweimal wiederholt — nach 0,5 s, dann 1 s —, bevor wir aufgeben und `error` auslösen. Ein Refresh
kostet also höchstens drei Aufrufe Ihres Endpunkts.

| Option       | Standard             | Bewirkt                                                                          |
| ------------ | -------------------- | -------------------------------------------------------------------------------- |
| `session`    | —                    | **Erforderlich.** Gibt eine Connect-Mode-Link-Session zurück.                    |
| `appearance` | unsere Standardwerte | Design-Tokens, angewendet in jedem Frame. Siehe [Erscheinungsbild](#appearance). |
| `css`        | keiner               | Ein CSS-String, angewendet in jedem Frame. Siehe [Eigenes CSS](#custom-css).     |
| `maxHeight`  | `600`                | Wie hoch ein Frame wachsen darf, bevor er stattdessen intern scrollt.            |

## Einen Slot mounten

Ein `mount` pro Slot. Fordern Sie ein Netzwerk an, mehrere oder alles, was die Session erlaubt —
die Granularität bestimmen Sie, ein Slot kann also eine einzelne Zeile in einer bestehenden Tabelle
sein oder ein Panel, das alles enthält.

```javascript theme={"system"}
connect.mount("#instagram-slot", { network: "instagram" });
connect.mount("#some-slot", { networks: ["facebook", "tiktok", "x"] });
connect.mount("#everything");

const row = connect.mount(document.querySelector("#tall"), { maxHeight: 1200 });
row.unmount();
```

`mount` nimmt einen CSS-Selektor oder ein Element entgegen und gibt `{ unmount, element }` zurück.
Es wirft eine Exception, wenn das Ziel nichts trifft — fast immer ein Slot, der noch nicht
existiert. Mounten Sie also, nachdem Ihr Markup im Dokument ist.

Jeder Mount ist ein iframe. Er meldet uns seine eigene Höhe, und wir passen ihn entsprechend an,
sodass Ihr Layout mit unseren Inhaltsänderungen umbricht; jenseits von `maxHeight` scrollt der
Frame intern, statt aus Ihrer Seite zu laufen. **Der schmalste unterstützte Slot ist 300px.**

Die Netzwerk-Keys sind Ayrshares eigene, und die Alias-Schreibweisen funktionieren ebenfalls:
`instagram` und `instagramapi` bedeuten beide `instagramApi`, und `x` bedeutet `twitter`. Ein Key,
der kein Netzwerk ist, rendert keine Kachel.

## Ihre eigene Schaltfläche

Ein Kunde, der lieber seine eigene Schaltfläche statt eines unserer Frames verwendet, ruft
stattdessen `popup` auf. Es führt denselben Ablauf aus, auf derselben Session, und meldet sich über
dieselben Handler.

<Warning>
  **Rufen Sie es direkt im Click-Handler auf, ohne dass davor etwas awaited wird.** Ein Browser
  erlaubt ein Popup nur, solange er den Klick Ihres Nutzers noch verarbeitet, und diese Erlaubnis
  überlebt kein `await`. Es muss ohnehin nichts awaited werden — die Session wurde bei `init`
  erstellt.
</Warning>

```javascript theme={"system"}
linkedInButton.addEventListener("click", () => {
  connect.popup({ network: "linkedin" });
});
```

`popup` gibt `{ close(), network }` zurück und gibt **immer** ein Handle zurück — auch nach einem
blockierten Popup, wo `close()` nichts tut —, sodass Ihr Code es vor einem `close()`-Aufruf nie auf
null prüfen muss.

Jedes Netzwerk funktioniert hier, auch die, die ein Frame in seinem eigenen Panel abschließt:
Facebook zeigt seinen Übergabe-Hinweis im Popup, Bluesky und X zeigen ihr Zugangsdaten-Formular,
und LinkedIn, Pinterest, YouTube und Google Business gehen zum Netzwerk hinaus und kommen zurück.

Es wirft synchron eine Exception für die drei Dinge, die Programmierfehler sind — kein `network`,
eine zerstörte Instanz oder eine Session, die noch nicht aufgelöst ist. Ein blockiertes Popup
gehört **nicht** dazu: Das löst `error` mit `reason: "popupBlocked"` aus, denn Ihr Nutzer hat
nichts falsch gemacht.

Es ist höchstens ein Popup gleichzeitig geöffnet. Ein zweiter Aufruf schließt das erste und meldet
darauf `cancelled` mit `reason: "superseded"`. Ein Popup, das einer unserer **Frames** geöffnet
hat, ist etwas anderes und wird nie angetastet — Ihre Schaltfläche kann also keinen Ablauf
abbrechen, der in einem gemounteten Slot läuft.

<Note>
  Ob die Session ein Netzwerk verknüpfen darf, beantwortet der **Server**, nicht das Skript. Eine
  auf Bluesky beschränkte Session, die nach LinkedIn gefragt wird, erhält eine im Popup gerenderte
  Ablehnung, die als `error` gemeldet wird. Das Skript prüft nur, dass überhaupt ein Netzwerk
  benannt wurde.
</Note>

## React

Das Skript ist Framework-frei, React braucht also nichts Besonderes von uns — aber vier Dinge an
seinem Lebenszyklus sollten Sie beim ersten Mal richtig machen.

Laden Sie das Skript **einmal**, außerhalb Ihres Komponentenbaums. In Next.js ist das
`next/script` in Ihrem Root-Layout; in Vite oder Create React App ein Tag in `index.html`. Es pro
Komponente zu laden führt es bei jedem Mount erneut aus.

```jsx ConnectAccounts.jsx theme={"system"}
import { useEffect, useRef, useState } from "react";

export function ConnectAccounts() {
  const slot = useRef(null);
  const [linked, setLinked] = useState([]);

  useEffect(() => {
    // Inside the effect, so the ref is attached: mount() throws if its target
    // does not exist yet, which is exactly what happens if you call it during render.
    const connect = window.AyrshareConnect.init({
      session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
      appearance: { "--ayr-connect-accent": "#0B7A6C" },
    });

    connect.mount(slot.current, { networks: ["instagram", "tiktok", "x"] });

    const stop = connect.on("success", ({ network }) => {
      setLinked(current => [...current, network]);
    });
    // `success` is one of ten events the widget reports. See Events below for
    // the full list, including `state`, which replaces polling.

    // destroy() takes the frames, the message listener and the timers with it.
    // Without this, a route change leaves all three behind.
    return () => {
      stop();
      connect.destroy();
    };
  }, []);

  return <div ref={slot} />;
}
```

<Warning>
  **Verwenden Sie eine Instanz nach `destroy()` nie wieder.** Eine zerstörte Instanz bleibt
  zerstört — `popup()` wirft darauf eine Exception, und `mount()` bringt sie nicht zurück.
  Erstellen Sie im nächsten Effect-Durchlauf eine neue Instanz, genau wie der Code oben.
</Warning>

Zwei Konsequenzen dieses Musters, beide keine Bugs:

* **In der Entwicklung sehen Sie den Session-Callback zweimal feuern.** Reacts Strict Mode führt
  Effects mount → unmount → mount aus, die Instanz wird also erstellt, zerstört und erneut
  erstellt. Das Cleanup oben macht das sicher; es kostet in der Entwicklung einen zusätzlichen
  Aufruf Ihres Backends und in der Produktion keinen.
* **Halten Sie die Abhängigkeiten des Effects stabil.** Ein Array-Literal, das aus einem
  Eltern-Render direkt in `mount` gereicht wird, ist bei jedem Render ein neuer Wert — ein Effect,
  der davon abhängt, reißt das Widget also bei jedem Render ab und baut es neu auf. Memoizen Sie
  es, oder halten Sie es konstant wie oben.

## Events

Abonnieren Sie mit `on`, das eine Unsubscribe-Funktion zurückgibt. Handler erhalten die
Event-Payload und den Mount, von dem sie kam; `off(name, handler)` erledigt dieselbe Aufgabe, wenn
Sie den Handler lieber benennen.

```javascript theme={"system"}
const stop = connect.on("success", ({ network, displayName }, mount) => {
  refreshRow(network, displayName, mount.element);
});
stop();

connect.on("error", ({ network, code, message }) => report(code, message));
connect.destroy(); // every frame, listener and timer
```

Zehn Events. Jedes trägt ein `network` — außer `ready` und außer der einen Art von `error`, die
sich gar nicht auf ein Netzwerk bezieht, siehe [`error` hat zwei Quellen](#error-has-two-sources)
unten.

| Event       | Feuert, wenn                                                            | Trägt außerdem                                 |
| ----------- | ----------------------------------------------------------------------- | ---------------------------------------------- |
| `ready`     | ein Frame gemountet und bereit ist                                      | —                                              |
| `click`     | Ihr Nutzer ein Netzwerk angeklickt hat, **in beide Richtungen**         | `action`: `"connect"` oder `"unlink"`          |
| `started`   | ein Verknüpfungsversuch läuft                                           | —                                              |
| `selection` | Ihr Nutzer eine Auswahl oder ein Formular erreicht hat                  | `step`                                         |
| `success`   | das Konto verbunden **und gespeichert** ist                             | `displayName` (fehlt, wenn unbekannt), `refId` |
| `unlinked`  | ein verbundenes Konto entfernt wurde und die Entfernung gespeichert ist | —                                              |
| `error`     | der Versuch fehlgeschlagen ist                                          | `code`, `message`, `reason`                    |
| `cancelled` | Ihr Nutzer abgebrochen hat                                              | `reason`                                       |
| `closed`    | das von diesem Versuch verwendete Popup geschlossen wurde               | —                                              |
| `state`     | der Kontostatus eines Netzwerks, beim Mount und bei jeder Änderung      | `state`, `since`                               |

**Vier davon sind Endungen** — `success`, `unlinked`, `error` und `cancelled` —, und genau eine
kommt pro Versuch an. `closed` ist eine Lifecycle-Meldung, die auf eine Endung folgt, statt selbst
eine zu sein.

`click` feuert, **bevor** irgendeine Verknüpfungsarbeit beginnt, meldet also auch einen Klick, den
ein blockiertes Popup oder eine tote Session anschließend verweigert. Es ist das Event für Ihre
eigenen Analysen; `started` ist das, das bedeutet, dass ein Versuch wirklich läuft.

### Gründe

| Event       | `reason`        | Bedeutet                                                                  |
| ----------- | --------------- | ------------------------------------------------------------------------- |
| `error`     | `popupBlocked`  | der Browser hat sich geweigert, das Popup zu öffnen                       |
| `cancelled` | `popupClosed`   | Ihr Nutzer hat das Fenster von Hand geschlossen                           |
| `cancelled` | `scopesDenied`  | Ihr Nutzer hat eine vom Netzwerk angefragte Berechtigung abgelehnt        |
| `cancelled` | `userCancelled` | Ihr Nutzer hat abgebrochen, oder Ihr Code hat `handle.close()` aufgerufen |
| `cancelled` | `superseded`    | ein zweiter `popup()`-Aufruf hat diesen Versuch ersetzt                   |

### `error` hat zwei Quellen

Nur eine davon ist ein Verknüpfungsfehler, und sie tragen unterschiedliche Felder.

* Ein **Verknüpfungsfehler** trägt `network`, `code` und den Mount, von dem er kam.
* Ein **Session-Fehler** — wir konnten Ihre Session nicht erstellen oder erneuern — trägt nur
  `message`, weil zu dem Zeitpunkt nichts verknüpft wurde.

Destrukturieren Sie defensiv: `code` und `network` sind bei der zweiten Art `undefined`.

### `state` erspart Ihnen das Polling

`state` ist der Datenkanal, kein Bericht über einen Versuch. Jeder Frame sendet beim Mounten eines
pro Netzwerk, mit dem aktuellen Status dieses Netzwerks und dem `since`-Zeitstempel, seit dem er
gilt, und ein weiteres bei jeder Statusänderung — **einschließlich Änderungen, die auf unserer
Seite entstehen**, etwa ein Token, das in „Neuverknüpfung erforderlich“ übergeht. Sie können also
Ihre gesamte UI aus dem Widget speisen, ohne irgendetwas zu pollen.

Die Werte sind dasselbe Enum, das
[`GET /profiles` mit `include=state`](/docs/apis/profiles/get-profiles) zurückgibt: `linked`,
`unlinked`, `identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`.

## Verknüpfung aufheben

Unsere Kacheln heben Verknüpfungen ebenso auf, wie sie sie herstellen. Ihr Nutzer klickt ein
verbundenes Netzwerk an, bestätigt, und das Konto wird entfernt:

1. `click` feuert mit `action: "unlink"`.
2. `unlinked` feuert, sobald die Entfernung gespeichert ist.

Ein Aufheben, das **fehlschlägt**, meldet `error`, und eines, aus dem Ihr Nutzer beim
Bestätigungsschritt aussteigt, meldet `cancelled`. Ein separates Unlink-Failed-Event gibt es nicht.

## Erscheinungsbild

Das Stylesheet eines Kunden kann nicht in einen Cross-Origin-Frame hineingreifen, deshalb reist das
Styling als Daten, die wir darin anwenden. Übergeben Sie `appearance` an `init` als CSS Custom
Properties; jede, die wir nicht erhalten, behält unseren Standardwert.

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-font-family": "Inter, system-ui, sans-serif",
    "--ayr-connect-surface-bg": "#111318",
    "--ayr-connect-surface-fg": "#F2F3F7",
    "--ayr-connect-accent": "#0B7A6C",
    "--ayr-connect-accent-fg": "#FFFFFF",
    "--ayr-connect-radius": "12px",
  },
});
```

<Warning>
  **Setzen Sie Farben paarweise.** Ein Hintergrund-Token ohne das zugehörige Vordergrund-Token ist
  der eine Weg, hiermit etwas Unlesbares zu erzeugen: Setzen Sie `--ayr-connect-surface-bg` allein
  auf einen dunklen Wert, bleibt unser Standard-`--ayr-connect-surface-fg` weiterhin dunkles
  Marineblau. Nichts kann die andere Hälfte für Sie ableiten.
</Warning>

Ganz ohne `appearance` behält jedes Token seinen Standardwert, und ein Frame sieht so aus:

<div className="my-8">
  <Frame caption="Standard-Tokens: weiße Flächen, dunkler marineblauer Text, Indigo-Akzent, 8px Radius.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-default.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=62f9ce7e061c8bb58132960b9e270f4a" alt="Drei Ayrshare-Connect-Kacheln mit dem Standard-Erscheinungsbild" width="920" height="528" data-path="images/multiple-users/connect-widget-appearance-default.webp" />
  </Frame>
</div>

Übergeben Sie eine Handvoll Tokens, und derselbe Frame nimmt Ihre Palette an. Dieses Beispiel färbt
die Flächen hellblau, vertieft Text und Akzent passend dazu und rundet die Ecken etwas stärker:

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-surface-bg": "#EFF6FF",
    "--ayr-connect-border-color": "#BFDBFE",
    "--ayr-connect-surface-fg": "#0F2A5F",
    "--ayr-connect-surface-fg-muted": "#4A6A9A",
    "--ayr-connect-accent": "#1D4ED8",
    "--ayr-connect-radius": "14px",
    "--ayr-connect-spacing": "10px",
  },
});
```

<div className="my-8">
  <Frame caption="Dieselben drei Kacheln nach Anwendung der Tokens oben.">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-themed.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=686d1ad900628a6d4c75dc242f1ab38c" alt="Drei Ayrshare-Connect-Kacheln, umgestylt mit hellblauen Flächen und einem tieferen Blau-Akzent" width="920" height="574" data-path="images/multiple-users/connect-widget-appearance-themed.webp" />
  </Frame>
</div>

**Es gibt eine Palette und kein Hell/Dunkel-Preset.** Nichts reagiert auf `prefers-color-scheme`,
und zwar absichtlich — das Theme Ihrer Seite muss nicht zum Betriebssystem Ihres Nutzers passen,
und eine Media Query würde die von Ihnen gewählten Farben stillschweigend überstimmen. Ein dunkles
Dashboard wird gethemt, indem Sie dunkle Werte übergeben.

Diese Token-Liste ist ein unterstützter Vertrag, den wir über Versionen hinweg beibehalten.

### Die Tokens

| Token                              | Standard                                                  |
| ---------------------------------- | --------------------------------------------------------- |
| `--ayr-connect-font-family`        | `Inter, "Segoe UI", system-ui, -apple-system, sans-serif` |
| `--ayr-connect-font-size`          | `16px`                                                    |
| `--ayr-connect-font-size-sm`       | `12px`                                                    |
| `--ayr-connect-label-font-weight`  | `600`                                                     |
| `--ayr-connect-status-font-size`   | `12px`                                                    |
| `--ayr-connect-status-font-weight` | `600`                                                     |
| `--ayr-connect-surface-bg`         | `#FFFFFF`                                                 |
| `--ayr-connect-surface-bg-hover`   | `#F7F7F7`                                                 |
| `--ayr-connect-surface-fg`         | `#010629`                                                 |
| `--ayr-connect-surface-fg-muted`   | `#56596F`                                                 |
| `--ayr-connect-border-color`       | `#DDDEE2`                                                 |
| `--ayr-connect-border-width`       | `1px`                                                     |
| `--ayr-connect-radius`             | `8px`                                                     |
| `--ayr-connect-spacing`            | `8px`                                                     |
| `--ayr-connect-accent`             | `#4553EE`                                                 |
| `--ayr-connect-accent-fg`          | `#FFFFFF`                                                 |
| `--ayr-connect-focus-ring-color`   | `#4553EE`                                                 |
| `--ayr-connect-focus-ring-width`   | `2px`                                                     |
| `--ayr-connect-disabled-fg`        | `#6E7185`                                                 |
| `--ayr-connect-status-radius`      | `4px`                                                     |
| `--ayr-connect-status-success-bg`  | `#EBFFF8`                                                 |
| `--ayr-connect-status-success-fg`  | `#237C5C`                                                 |
| `--ayr-connect-status-warning-bg`  | `#FFF7EF`                                                 |
| `--ayr-connect-status-warning-fg`  | `#702E00`                                                 |
| `--ayr-connect-status-critical-bg` | `#FFE5E1`                                                 |
| `--ayr-connect-status-critical-fg` | `#AD1902`                                                 |
| `--ayr-connect-status-info-bg`     | `#ECF9FF`                                                 |
| `--ayr-connect-status-info-fg`     | `#1F6686`                                                 |
| `--ayr-connect-callout-bg`         | `#FFF7EF`                                                 |
| `--ayr-connect-callout-fg`         | `#702E00`                                                 |
| `--ayr-connect-partner-name`       | `""`                                                      |
| `--ayr-connect-icon-size`          | `32px`                                                    |
| `--ayr-connect-avatar-size`        | `40px`                                                    |

Ein Wert, der für sein Token kein gültiges CSS ist, wird **ignoriert, mit einer Warnung in Ihrer
Konsole**, statt angewendet zu werden. Das ist wichtiger, als es klingt: Ein einheitenloses `8` für
`--ayr-connect-spacing` ist ein völlig harmloser String, der jede darauf basierende Berechnung
ungültig machen und das Layout kollabieren lassen würde, ohne irgendwo einen Fehler. Geben Sie
Längen eine Einheit.

### Ihr eigener Name auf dem Übergabebildschirm

Bevor wir Ihren Nutzer an ein Netzwerk übergeben, zeigen wir einen kurzen Bildschirm, der benennt,
über wen die Verbindung läuft. Zwei Hooks machen ihn zu Ihrem eigenen, und beide sind
versionsstabil.

`--ayr-connect-partner-name` setzt das Label. Es ist das eine Token, dessen Wert **Text** ist, er
muss also als CSS-String in Anführungszeichen stehen — ein Wert ohne Anführungszeichen ist ungültig
und rendert gar nichts:

```javascript theme={"system"}
AyrshareConnect.init({
  session: getSession,
  appearance: { "--ayr-connect-partner-name": "'Acme Social'" },
  css: "[data-ayr-connect-partner-mark]::after { background-image: url('https://cdn.example.com/mark.svg') }",
});
```

Das Logo ist kein Token. Wir liefern die Marke als positioniertes, dimensioniertes Element aus, und
Sie füllen es über `css` mit einem `background-image`, wie oben — ein Token, das aus unserem
Dokument heraus ein Bild laden könnte, akzeptieren wir nicht. Die Anfrage kommt also aus einer
Regel, die Sie geschrieben haben, statt aus einem Wert, den Sie uns übergeben haben.

<Note>
  `[data-ayr-connect-partner-mark]` und `[data-ayr-connect-partner-name]` sind die **Ausnahme** vom
  Custom-CSS-Vorbehalt unten: Diese beiden Selektoren sind Teil des Vertrags, und wir behalten sie
  über Versionen hinweg bei.
</Note>

### Eigenes CSS

`css` nimmt einen String entgegen, der in jedem Frame angewendet wird — für die Fälle, die Tokens
nicht abdecken.

```javascript theme={"system"}
AyrshareConnect.init({ session: getSession, css: "button { letter-spacing: 0.01em }" });
```

<Warning>
  **Eigenes CSS wird über Versionen hinweg nicht unterstützt.** Seine Selektoren zielen auf unser
  internes Markup, das sich zwischen Releases ändert — eine Regel, die heute funktioniert, kann
  nach jedem Update stillschweigend nicht mehr greifen. Der Token-Vertrag oben ist der Teil, den
  wir beibehalten. Pinnen Sie eine Version, wenn Sie auf eigenes CSS angewiesen sind.
</Warning>

Popups erben `appearance` und `css` der Instanz genauso wie Frames, sodass Ihr Nutzer nicht
mitten in einem Ablauf unsere neutralen Standardwerte zu sehen bekommt.

## Wissenswertes vor dem Release

<AccordionGroup>
  <Accordion title="Ein Popup mit abgelaufener Session meldet cancelled, nicht error">
    Einem mit `popup()` geöffneten Popup kann nicht mitgeteilt werden, warum ein Token abgelehnt
    wurde — dafür müsste es einer Origin vertrauen, die es noch nicht validiert hat, was unser
    Sicherheitsmodell nicht erlaubt. Ein Popup mit totem Token schließt sich also und erscheint als
    `cancelled` mit `reason: "popupClosed"` statt als `error`.

    In der Praxis ist das selten: Ein vor einem stillen Refresh geöffnetes Popup funktioniert
    weiter, weil es sein Token beim Öffnen validiert hat. Sehen Sie unerklärte
    `popupClosed`-Ergebnisse, prüfen Sie, ob Ihr `session`-Endpunkt eine frische Session
    zurückgibt.
  </Accordion>

  <Accordion title="Eine Session pro Instanz, nicht eine pro Mount">
    Vierzehn Mounts teilen sich ein Token und kosten einen Aufruf Ihres Backends, nicht vierzehn.
    Wenn Sie unterschiedlich beschränkte Slots möchten — ein anderes `allowedSocial` auf einigen
    davon —, starten Sie ein zweites `init` mit eigener Session, statt zu erwarten, dass ein Mount
    sie einschränkt.
  </Accordion>

  <Accordion title="Die Frames rendern nur auf der Origin, die Sie deklariert haben">
    Jede Session trägt die `origin`, auf der Ihre Seite läuft, und ein Frame vergleicht die Seite,
    die ihn einbettet, mit diesem Wert, bevor er irgendetwas rendert. Ein anderswo eingebetteter
    Frame bleibt leer und sendet keine Events. Es gibt keine Allowlist zu registrieren und nichts
    zu konfigurieren — senden Sie die richtige `origin`, wenn Sie die Session erstellen.
  </Accordion>

  <Accordion title="Die Höhe wird für Sie verwaltet und ist kein Event">
    Frames melden ihre Höhe an das Skript, und das Skript passt sie an. Ihr Layout bricht einfach
    um. Es gibt kein Resize-Event zum Abonnieren und nichts, das Sie auf Ihrer Seite messen
    müssten.
  </Accordion>
</AccordionGroup>

## Voraussetzungen

<ul className="custom-bullets">
  <li>
    Der **[Max Pack](/docs/additional/maxpack)**. Eine Widget-Session ist eine Connect-Mode-Session, und
    eine ohne Max Pack zu erstellen gibt `code: 504` zurück. Kontaktieren Sie den Support, wenn Sie
    den Connect Mode auf einem Account ohne Max Pack aktivieren müssen.
  </li>

  <li>
    Ein **`origin`** auf jeder Session — die exakte Origin, auf der Ihre Seite läuft. Weglassen
    gibt `code: 505` zurück; ein Wert, der keine `https`-Origin, kein Custom Scheme und nicht
    `http://localhost` ist, gibt `code: 506` zurück.
  </li>

  <li>
    **Kein `network`** auf der Session. Dieser Parameter macht eine Session stattdessen zu
    [Direct Mode](/docs/multiple-users/connect-direct-mode), und die URL einer Direct-Mode-Session ist
    nicht das, was das Skript erwartet.
  </li>
</ul>

Jeder der oben genannten Codes steht in der Referenz
[Link-Session-Fehler](/docs/errors/errors-ayrshare#link-session-errors), mit der exakten Meldung, die
die API zurückgibt, und was dagegen zu tun ist.
