Eingebettetes Signieren
Ihre Kunden unterschreiben direkt in Ihrer Anwendung: Sie zeigen unsere Signierseite in einem iframe auf Ihrer eigenen Website. Das geht in allen Stufen, in denen externe Empfänger handeln (EES, FES), und für Freigebende und Betrachter. Qualifizierte Unterschriften (QES) werden nie eingebettet – Mitglieder mit Zertifikat unterschreiben im Dashboard oder über den Freigabe-Link.
Maßgeblich für Ihre Geschäftslogik bleibt der Webhook (bzw. GET /v1/events). Die Nachrichten der eingebetteten Seite sind Hinweise für Ihre Oberfläche, keine Bestätigung.
1. Erlaubte Websites festlegen
Nur Websites auf der Liste Ihrer Organisation dürfen die Signierseite einbetten. Die Liste ist leer, bis Sie sie füllen – leer heißt: Einbettung aus.
PUT /v1/embed-settings
Authorization: Bearer qgw_live_…
Idempotency-Key: 5d1f…
Content-Type: application/json
{ "allowedOrigins": ["https://app.example.com", "https://*.beispiel.de"] }
Antwort:
{ "object": "embed_settings", "allowedOrigins": ["https://app.example.com", "https://*.beispiel.de"] }
Regeln für jeden Eintrag:
https://host[:port]ohne Pfad, Query oder Zugangsdaten, oderhttps://*.hostfür alle Unterdomains einer eigenen Domain (nicht*.vercel.app, nicht*.co.ukund ähnliche öffentliche Endungen).- Kein
*allein, keine IP-Adressen, keinhttp://– außerhttp://localhost:<port>mit einem Test-Schlüssel; solche Einträge gelten nur im Testmodus. - Höchstens 20 Einträge, jeder nur einmal. Ein abgelehnter Eintrag ergibt 422 / 9012 mit
details.reason(origin_invalid,origin_duplicate) unddetails.index. - Scope
org:managezum Ändern (Standard aus),org:readzum Lesen (GET /v1/embed-settings). Im Dashboard ändern Inhaber und Administratoren die Liste. - Jede Änderung steht im Aktivitätsprotokoll (
EMBED_SETTINGS_UPDATED).
Änderungen gelten sofort für unsere Signierseite und für neue Einbettungs-Sitzungen. Für den Unterschriftsschritt bei EES/FES erhält der Signaturdienst die Liste beim Start der Unterschriftsrunde; eine spätere Änderung wirkt erst für neue Runden. Dort zählen nur exakte Origins (Einträge mit * erlauben nur unsere Seite, nicht den Unterschriftsschritt), und je Runde höchstens neun Ihrer Origins.
Für Signierseite, Einbettungs-Sitzungen und Unterschriftsschritt gilt dieselbe Regel je Modus: Im Live-Modus zählen nur die https-Einträge, http://localhost:<port> und andere Nur-Test-Einträge ausschließlich im Testmodus. Ist die Einbettung für Ihre Organisation ausgeschaltet (Stufenfunktion) oder im Live-Modus noch gesperrt, erhält auch der Unterschriftsschritt keine Ihrer Origins. Qualifizierte Unterschriften (QES) werden nie eingebettet.
2. Einbettungs-Sitzung anfordern
Sobald ein Empfänger an der Reihe ist („dran“, Ereignis recipient.ready), fordert Ihr Server eine Sitzung für die Website an, auf der Sie die Seite zeigen:
POST /v1/envelopes/{id}/recipients/{rid}/embedded-session
Authorization: Bearer qgw_test_…
Idempotency-Key: 8c2e…
Content-Type: application/json
{ "origin": "https://app.example.com", "locale": "de" }
{
"object": "embedded_session",
"url": "https://sign.<domain>/s/<token>?embed=1",
"expiresAt": "2026-09-30T12:05:00Z",
"origin": "https://app.example.com"
}
- Scope
envelopes:signing_urls(Standard aus), Rate-Limitwrite. - Die URL gilt 5 Minuten und genau einmal und nur eingebettet auf dieser Origin. Beim ersten Laden tauscht die Seite sie gegen eine Sitzung, die sie nur im Speicher hält; ein zweiter Aufruf zeigt „Dieser Link ist nicht gültig“, ohne zu verraten, ob es den Vorgang gibt. Fordern Sie für jede Anzeige eine neue Sitzung an.
- Der Link der Einladungsmail bleibt gültig.
- Die Antwort wird nie gespeichert (sie enthält einen Link): Ein wiederholter Aufruf mit demselben
Idempotency-Keylegt eine neue Sitzung an; der Schlüssel schützt nur vor gleichzeitigen Doppelaufrufen (409 / 9008). - Behandeln Sie die URL wie ein Passwort: nicht loggen, nicht weitergeben, nicht in Ihrer Browser-Anwendung erzeugen (nur auf Ihrem Server).
- Jede Sitzung steht im Nachweisprotokoll des Vorgangs (
LINK_ISSUED, ohne die URL).
Mögliche Fehler, in dieser Reihenfolge geprüft:
| Status / Code | Bedeutung |
|---|---|
| 404 | Vorgang oder Empfänger gehören nicht zu Ihrer Organisation oder nicht zum Modus des Schlüssels |
| 403 / 9039 | Einbettung ist für Ihre Organisation abgeschaltet |
| 403 / 9038 | origin steht nicht auf der Liste (auch: Platzhalter statt exakter Origin, http://localhost im Live-Modus) |
422 / 9012 qes_not_embeddable | Der Empfänger unterschreibt qualifiziert (QES) |
| 409 / 9033 | Der Empfänger ist nicht an der Reihe (details.reason) |
409 / 9030 signing_page_disabled | Die Signierseite ist abgeschaltet |
403 / 9030 embedding_live_disabled | Live-Schlüssel: Einbettung ist für den Live-Betrieb noch nicht freigegeben |
| 403 | Dem Schlüssel fehlt envelopes:signing_urls |
Ein Signierlink (POST …/signing-url) oder der Link der Einladungsmail lässt sich ebenfalls einbetten: Hängen Sie ?embed=1 an. Dann darf jede Website Ihrer Liste die Seite zeigen, und der Link gilt wie gewohnt (15 Minuten bis zum ersten Öffnen, danach 60 Minuten Sitzung). Die Einbettungs-Sitzung ist vorzuziehen, weil sie an genau eine Website gebunden ist.
3. Seite anzeigen mit dem Widget
<div id="unterschrift"></div>
<script src="https://sign.<domain>/embed/v1/onesign.js"></script>
<script>
const widget = OneSign.mountSigning(document.getElementById('unterschrift'), {
url: sessionUrl, // die URL aus Schritt 2, von Ihrem Server
minHeight: 600,
title: 'Mietvertrag unterschreiben',
onEvent(event) {
if (event.type === 'completed') zeigeAbschluss();
if (event.type === 'declined') zeigeAblehnung();
if (event.type === 'error') zeigeFehler(event.code);
},
});
// widget.destroy() entfernt das iframe wieder.
</script>
Das Widget hat keine Abhängigkeiten und setzt keine Cookies. Es erzeugt das iframe mit allow="camera; microphone; fullscreen" (die Identitätsprüfung der FES braucht die Kamera auf jeder Rahmen-Ebene) und referrerpolicy="no-referrer", passt die Höhe an (resize) und gibt nur Nachrichten weiter, die von der Origin der Signierseite stammen und zum Protokoll passen.
Ohne Widget: Ein eigenes iframe mit denselben Attributen funktioniert ebenso; hören Sie dann selbst auf message und prüfen Sie event.origin gegen die Origin der URL sowie source und v der Nachricht.
4. Nachrichten der Seite
Jede Nachricht hat die Form { source: "onesign", v: 1, type, …, at } und enthält nie Namen, E-Mail-Adressen, Tokens oder Links. Die Seite schickt sie nur an die gebundene Origin (bzw. die geprüfte Website Ihrer Liste), nie an *.
type | Zusatzfelder | Bedeutung |
|---|---|---|
ready | envelopeId, recipientId | Seite geladen |
resize | height | Höhe der Seite in Pixeln |
signed | envelopeId, recipientId | Der Empfänger hat unterschrieben |
approved | envelopeId, recipientId | Der Empfänger hat freigegeben |
viewed | envelopeId, recipientId | Der Empfänger hat das Ansehen bestätigt |
declined | envelopeId, recipientId | Der Vorgang wurde abgelehnt (auch im Unterschriftsschritt) |
completed | envelopeId | Der Vorgang ist abgeschlossen |
expired | envelopeId, recipientId | Der Vorgang ist abgelaufen |
error | code | link_invalid, rate_limited, unavailable, action_failed, envelope_cancelled, envelope_failed, qes_new_window |
envelopeId und recipientId stehen nur in Nachrichten einer Einbettungs-Sitzung; bei einem Signierlink mit ?embed=1 fehlen sie. Die Seite leitet im eingebetteten Modus nie selbst weiter – reagieren Sie auf completed (oder den Webhook). Datenschutzhinweis und Fußzeile mit Impressum bleiben im Rahmen sichtbar.
5. Sicherheit und Browser
- Clickjacking-Schutz: Die Signierseite erlaubt das Einbetten (
Content-Security-Policy: frame-ancestors) nur der gebundenen Origin bzw. den Websites Ihrer Liste; ohne?embed=1ist sie nie einbettbar. - Keine Cookies: Browser blockieren Cookies in fremden Rahmen (z. B. Safari). Die Seite braucht keine – die Sitzung lebt im Speicher der Seite.
- QES: Öffnet ein eingebetteter Link den Schritt eines Mitglieds mit qualifizierter Unterschrift, zeigt die Seite „In neuem Fenster anmelden“ (öffnet ein neues Fenster) und meldet
errormitqes_new_window. - Live-Betrieb: Im Live-Modus ist die Einbettung noch gesperrt (403 / 9030
embedding_live_disabled), bis sie auf echten Geräten geprüft ist. Im Testmodus steht alles zur Verfügung.