---
title: "Web-SDK – Relyo"
description: "Relyo in eine Website einbauen: Script oder ESM, Support-Link, React/Next.js, Optionen, Maskierung, CSP und Grenzen."
lang: de
canonical: https://relyo.de/docs/web
---

# Websites

So baust du Co-Browsing für den Support in eine Website ein. Das Web-SDK läuft in jedem aktuellen Browser und braucht kein Framework. Es spiegelt den Tab als Seitenstruktur (DOM, mit [rrweb](https://www.rrweb.io/)), nicht als Video: Das spart Bandbreite und hält Text scharf.

## Mit einem Coding-Agent einbauen

Kopiere diesen Prompt in deinen Coding-Agent (Claude Code, Cursor, Copilot …) und ergänze den Projekt-Key, falls du einen hast:

```text
Baue Relyo-Co-Browsing-Support in diese Website ein.
Lies zuerst die Anleitung https://relyo.de/.well-known/agent-skills/relyo-integrate/SKILL.md
und die Web-Doku https://relyo.de/docs/web.md, dann:

1. Das SDK erst beim Klick auf „Support“ laden (Abschnitt „Einbinden“, Variante „Erst beim Klick laden“),
   relayUrl 'wss://relyo.de', projectKey: 'PROJEKT_KEY' (weglassen, wenn keiner vorhanden ist).
2. Einen unauffälligen Link „Support“ in den Footer setzen, der showCode() aufruft.
3. Sensible Bereiche (Konto-, Zahlungs-, persönliche Daten) mit der Klasse relyo-redact oder redactSelector maskieren.
4. Eine vorhandene Content-Security-Policy um https://relyo.de (script-src) und wss://relyo.de (connect-src) ergänzen.
5. Lint, Typecheck und Tests ausführen und zusammenfassen, was geändert wurde.
Starte selbst keine Support-Session und gib keine Support-Codes ein.
```

## Einbinden

### Per Script

```html
<script src="https://relyo.de/sdk/relyo.js"></script>
<script>
  const relyo = Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt' });
</script>
```

Das Script legt das globale Objekt `Relyo` an (rund 40 kB gzip). `Relyo.init` darf pro Seite einmal aufgerufen werden; ein zweiter Aufruf liefert dieselbe Instanz zurück.

Zwei Adressen stehen zur Wahl:

| Adresse                                | Verhalten                                                                       |
| -------------------------------------- | ------------------------------------------------------------------------------- |
| `https://relyo.de/sdk/relyo.js`        | immer der aktuelle Stand, Browser fragen jedes Mal nach Änderungen (`no-cache`) |
| `https://relyo.de/sdk/v0.1.0/relyo.js` | feste Version, ein Jahr im Cache                                                |

### Erst beim Klick laden

Am sparsamsten: Das SDK wird erst geladen, wenn jemand auf „Support“ tippt. Bis dahin gibt es keinen einzigen Request an relyo.de. So ist der Support-Link auf [jochen-maurer.com](https://jochen-maurer.com) eingebaut.

```html
<a href="mailto:support@example.com" id="support">Support</a>

<script>
  let relyo;
  function loadRelyo() {
    relyo ??= new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://relyo.de/sdk/relyo.js';
      script.onload = () =>
        resolve(
          Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt', connect: 'lazy' }),
        );
      script.onerror = () => {
        relyo = undefined;
        reject(new Error('Relyo nicht erreichbar'));
      };
      document.head.append(script);
    });
    return relyo;
  }

  document.querySelector('#support').addEventListener('click', (event) => {
    event.preventDefault();
    const fallback = event.currentTarget.href; // Ausweg, falls relyo.de nicht erreichbar ist
    loadRelyo()
      .then((r) => r.showCode())
      .catch(() => (location.href = fallback));
  });
</script>
```

### Als Modul

`relyo.mjs` ist dasselbe SDK als ES-Modul und lässt sich von jeder Seite nachladen:

```js
const { Relyo } = await import('https://relyo.de/sdk/relyo.mjs');
const relyo = Relyo.init({ relayUrl: 'wss://relyo.de', projectKey: 'pk_deinprojekt' });
```

Ein npm-Paket gibt es noch nicht.

### React und Next.js

Das SDK braucht `window` und `document`. In Next.js gehört es deshalb in eine Client-Komponente und wird erst beim Klick geladen, nie beim Server-Rendering.

```tsx
'use client';

interface RelyoInstance {
  showCode(): void;
}
declare global {
  interface Window {
    Relyo?: { init(options: Record<string, unknown>): RelyoInstance };
  }
}

let relyo: Promise<RelyoInstance> | undefined;

function loadRelyo(): Promise<RelyoInstance> {
  relyo ??= new Promise<RelyoInstance>((resolve, reject) => {
    const script = document.createElement('script');
    script.src = 'https://relyo.de/sdk/relyo.js';
    script.onload = () =>
      window.Relyo
        ? resolve(
            window.Relyo.init({
              relayUrl: 'wss://relyo.de',
              projectKey: 'pk_deinprojekt',
              connect: 'lazy',
            }),
          )
        : reject(new Error('Relyo nicht geladen'));
    script.onerror = () => reject(new Error('Relyo nicht erreichbar'));
    document.head.append(script);
  }).catch((err: unknown) => {
    relyo = undefined;
    throw err;
  });
  return relyo;
}

export function SupportLink() {
  return (
    <button type="button" onClick={() => loadRelyo().then((r) => r.showCode())}>
      Support
    </button>
  );
}
```

Bei Seitenwechseln innerhalb der App (React Router, Next.js-Links) braucht Relyo nichts weiter: Das SDK bemerkt neue Adressen selbst.

## Optionen

`Relyo.init` nimmt ein Objekt mit diesen Feldern:

| Option           | Standard   | Bedeutung                                                                                                                                                                                              |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `relayUrl`       | –          | Pflicht. Adresse des Servers, zum Beispiel `wss://relyo.de`; `https://` wird zu `wss://`.                                                                                                              |
| `projectKey`     | –          | Projekt-Key deiner Website. Ohne Key ordnet ein Server mit offenem Dienst die Seite ihrer Adresse (Origin) zu.                                                                                         |
| `user`           | –          | Angemeldeter Nutzer, `{ id, name }`. Nur zur Anzeige für deine Supporter, nicht geprüft. Siehe [Angemeldete Nutzer](#angemeldete-nutzer).                                                              |
| `connect`        | `'auto'`   | Wann das SDK sich verbindet, siehe [Verbindung](#verbindung).                                                                                                                                          |
| `redactSelector` | –          | Zusätzlicher CSS-Selektor für Bereiche, die maskiert werden, zum Beispiel `'.kontodaten, [data-private]'`. `.relyo-redact` gilt immer.                                                                 |
| `maskAllInputs`  | `false`    | `true` maskiert alle Eingabefelder, nicht nur die sensiblen.                                                                                                                                           |
| `consentText`    | –          | Eigener Erklärtext im Freigabe-Dialog; `{agent}` wird durch den Namen des Supporters ersetzt.                                                                                                          |
| `cspNonce`       | –          | Nonce für das Style-Element des SDK, wenn deine CSP Inline-Styles nur mit Nonce erlaubt.                                                                                                               |
| `appVersion`     | –          | Version deiner Seite; erscheint in der Konsole.                                                                                                                                                        |
| `statusPosition` | `'bottom'` | Startposition der Status-Pille: `'bottom'`, `'bottom-left'`, `'bottom-right'`, `'top'`, `'top-left'` oder `'top-right'`. Zum Beispiel `'top-right'`, wenn deine Seite unten eine feste Navigation hat. |
| `locale`         | `'de'`     | Sprache der Dialoge. Bisher gibt es nur Deutsch.                                                                                                                                                       |

`Relyo.init` liefert eine Instanz mit diesen Methoden:

| Methode          | Wirkung                                                                               |
| ---------------- | ------------------------------------------------------------------------------------- |
| `showCode()`     | fordert einen Code an und zeigt ihn im Dialog                                         |
| `end()`          | beendet eine laufende Session bzw. bricht Code oder Freigabe ab                       |
| `identify(user)` | setzt den angemeldeten Nutzer, `identify(null)` beim Logout                           |
| `destroy()`      | baut das SDK ab; eine laufende Session kann ein neues `init` im selben Tab fortsetzen |

## Verbindung

| `connect` | Verhalten                                                                                                |
| --------- | -------------------------------------------------------------------------------------------------------- |
| `'auto'`  | mit `user` sofort verbinden (das Gerät erscheint in der Konsole), anonyme Besucher erst bei `showCode()` |
| `'eager'` | immer sofort verbinden, auch anonym                                                                      |
| `'lazy'`  | nur bei `showCode()`; die Verbindung schließt wieder, wenn Code oder Session enden                       |

Für einen Support-Link im Footer ist `'lazy'` richtig. Vor der ersten Verbindung legt das SDK nichts im Browser ab, auch keine Kennung.

## Angemeldete Nutzer

Mit `user` (oder später `identify`) erscheint das Gerät angemeldeter Nutzer in der Geräteliste der Konsole. Supporter mit eigenem Zugang können dort eine Session anfragen, ohne dass der Nutzer einen Code vorliest; die Freigabe auf dem Gerät bleibt Pflicht.

```js
relyo.identify({ id: 'u_123', name: 'Max M.' }); // nach dem Login
relyo.identify(null); // beim Logout
```

Die Geräteliste gibt es nur für Projekte mit Projekt-Key, nicht im offenen Dienst. `id` und `name` sind reine Anzeige; schicke keine Daten mit, die deine Supporter nicht sehen sollen.

## Maskierung

Maskiert wird im Browser des Nutzers, bevor Daten ihn verlassen:

- **Immer:** Felder mit `type="password"`, `type="email"` und `type="tel"`, Felder mit `autocomplete="cc-…"` (Kartendaten) und `autocomplete="one-time-code"`.
- **`.relyo-redact`:** Elemente mit dieser Klasse und alles darin. Der Supporter sieht an ihrer Stelle eine leere Fläche; Eingaben darin kommen als `*` an.
- **`redactSelector`:** wirkt wie `.relyo-redact` für eigene Selektoren.
- **`maskAllInputs: true`:** alle Eingabefelder.

```html
<div class="relyo-redact">IBAN: DE12 3456 …</div>
```

Maskiere alles mit sensiblen Daten, was die Regeln oben nicht erfassen, etwa Gesundheitsdaten oder Ausweisnummern in normalen Textfeldern. Ferngesteuerte Eingaben in maskierte Felder lehnt das SDK ab.

## Content-Security-Policy

Hat deine Seite eine CSP, braucht Relyo:

| Direktive     | Wert                                                     | Wofür                                     |
| ------------- | -------------------------------------------------------- | ----------------------------------------- |
| `script-src`  | `https://relyo.de`                                       | das Script bzw. Modul                     |
| `connect-src` | `wss://relyo.de`                                         | die Verbindung zum Server                 |
| `style-src`   | `'unsafe-inline'` oder `'nonce-…'` mit Option `cspNonce` | die Dialoge des SDK (in einem Shadow DOM) |

Mit eigenem Server setzt du dessen Adresse statt `relyo.de` ein.

## Ablauf und Seitenwechsel

- **Freigabe:** Erst nach „Freigeben“ startet die Übertragung. Der Dialog nennt, was geteilt wird (dieser Tab) und was nicht.
- **Statusanzeige:** Während der Session zeigt ein roter Rahmen um das Fenster, dass geteilt wird; er verdeckt nichts. Dazu kommt eine kleine Pille mit dem Namen des Supporters und „Beenden“, standardmäßig unten mittig. Nutzer können sie an eine andere Ecke oder Kante ziehen. Abschalten lässt sich beides nicht.
- **Neu laden und Links:** Lädt die Seite neu oder wechselt der Nutzer per Link auf eine andere Seite mit Relyo, setzt das SDK die Session im selben Tab fort. Der Server hält sie 30 Sekunden offen.
- **Steuerung:** Klicks, Eingaben, Scrollen und die Tasten Enter, Escape, Tab und Pfeile. Jeder eigene Klick oder Tastendruck des Nutzers beendet die Steuerung sofort.

## Grenzen

- Inhalte aus fremden iframes (zum Beispiel eingebettete Videos oder Bezahlformulare) erscheinen als leere Fläche.
- `<video>` und `<canvas>` werden nicht gespiegelt.
- Gesteuerte Tasten lösen keine Browser-Standardaktionen aus: Tab bewegt den Fokus nicht, Pfeiltasten scrollen nicht. Enter in einem Formularfeld schickt das Formular ab.
- Die Dialoge sind bisher nur auf Deutsch.
- Bilder werden mitgeschickt, damit der Supporter sie auch hinter einem Login sieht. Seiten mit vielen großen Bildern brauchen entsprechend Bandbreite.

## Fehlermeldungen

Kann der Code nicht angefordert werden, zeigt der Dialog einen Hinweis statt des Codes:

| Meldung                                                | Ursache                                                                                                   |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| „Keine Verbindung zum Support-Server …“                | Server nicht erreichbar, Netzwerk oder CSP blockiert, oder zu viele Verbindungen von derselben IP-Adresse |
| „Gerade sind alle Support-Plätze belegt …“             | Limit für gleichzeitige Sessions erreicht                                                                 |
| „Support ist für diese Seite derzeit nicht verfügbar.“ | Seite gesperrt                                                                                            |
| „Der Support-Code konnte nicht angefordert werden.“    | zum Beispiel ein unbekannter Projekt-Key oder eine Adresse, die nicht zum Key passt                       |
