# Einbetten und Ausweisprüfung

> Das Beitrittsformular auf Ihrer Website, das öffentliche Profil des Vereins und die Ausweisprüfung online und offline.

URL: https://developers.vimine.network/de/guides/embed-and-verify/

## Das Beitrittsformular auf Ihrer Website

Jeder Verein, der den öffentlichen Beitritt aktiviert hat, hat eine Seite `https://app.vimine.network/enroll/<slug>`,
die sich in einem iframe auf der Website des Vereins platzieren lässt:

```html
<iframe src="https://app.vimine.network/enroll/your-slug" style="width:100%;min-height:900px;border:0"></iframe>
```

Der Verein muss die Domain der Website in seinen Einstellungen unter seinen **Einbettungsdomains** eintragen. Die Anmelde- und
Einwilligungsseite von *Mit Vimine anmelden* dagegen lässt sich **nicht** in einen iframe setzen: Sie ist immer eine
ganze Seite auf vimine.network.

`?locale=en` öffnet das Formular auf Englisch (das Element `<vimine-enroll>` übergibt es selbst, aus der Sprache der Seite).

### Das Aussehen des Formulars

Das Formular kann die Farben Ihrer Website übernehmen. Eine Seite in einem iframe sieht das CSS der umgebenden Seite nicht, also reist der Stil
in der Adresse mit:

| Parameter | Werte | |
|---|---|---|
| `scheme` | `light`, `dark` | das Farbschema der Seite; ohne den Parameter das des Hintergrunds oder des Besuchers |
| `accent` | eine Hex-Farbe (`1e3a5f`) | Schaltflächen, Links, fokussierte Felder und der Name des Vereins |
| `background` | eine Hex-Farbe oder `transparent` | der Grund hinter den Karten; `transparent` lässt Ihre Seite durchscheinen |
| `font` | `inter`, `roboto`, `open-sans`, `montserrat`, `nunito`, `lora`, `merriweather`, `roboto-slab`, `system` | die Schrift der ganzen Seite, Felder eingeschlossen, unter denen, die Vimine selbst ausliefert (`system`: die des Geräts) |

```html
<iframe src="https://app.vimine.network/enroll/your-slug?scheme=light&accent=1e3a5f&background=transparent"
        style="width:100%;min-height:900px;border:0"></iframe>
```

Andere Werte werden ignoriert. Mit `<vimine-enroll>` gibt es keine Adresse zu schreiben: Setzen Sie `--vimine-scheme`, `--vimine-accent`,
`--vimine-background` und `--vimine-typeface` in Ihrem CSS ([Elemente](https://developers.vimine.network/de/sdk/elements/)). Mit `transparent` wählen Sie das Farbschema, das auf
Ihrer Seite lesbar ist: Die Karten des Formulars haben ihre eigene Füllung, der Text außerhalb liegt auf Ihrer.

### Wer schon ein Vimine-Konto hat

Eine Identität gilt für jeden Verein: Wer schon ein Konto hat, muss das Formular nicht noch einmal ausfüllen. Direkt
geöffnet, erkennt die Seite `…/enroll/<slug>` die Sitzung und zeigt **einen einzigen Bildschirm** — die Mitgliedsart, das Statuto
und die Datenschutzhinweise *dieses* Vereins, und nur das, was dieser Verein zusätzlich verlangt (die Adresse, wenn er
erklärt hat, dass er sie erfasst). Statuto und Datenschutzhinweise werden jedes Mal akzeptiert: Nichts geht von einem
Verein auf einen anderen über.

In einem iframe lässt sich die Sitzung nicht lesen: Ein Browser hält eine eingebettete Seite getrennt von derselben direkt geöffneten Website.
Deshalb bietet das eingebettete Formular *Haben Sie schon ein Vimine-Konto? Weiter auf Vimine* an, das die Seite in einem neuen Tab öffnet. Wenn Sie
den iframe von Hand schreiben und `sandbox` verwenden, nehmen Sie `allow-popups` und `allow-popups-to-escape-sandbox` auf, sonst
bleibt dieser Tab in der Sandbox gefangen; `<vimine-enroll>` tut das bereits.

## Das öffentliche Profil

`GET /v1/aps/{slug}` liefert das öffentliche Profil des Vereins — Erscheinungsbild, Mitgliedsarten mit ihren Beiträgen,
geltendes Statuto —, falls Sie ein eigenes Erlebnis bauen wollen:

```js
import { publicApi } from '@vimine/sdk';

const { aps, membership_types, statuto } = await publicApi().association('your-slug');
```

Ohne Zugangsdaten. Auf einer Seite, auf der sich das Mitglied mit *Mit Vimine anmelden* angemeldet hat, können Sie dann den Antrag entgegennehmen:

```js
const { aps, membership_types, statuto, privacy_notice } = await publicApi().association('your-slug');

await vimine.membership.request({
    membership_type,
    accept_statuto: { version: statuto.version },
    // die Datenschutzhinweise des Vereins, die Ihre Seite gezeigt hat
    privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256, locale: privacy_notice.locale },
    // die Adresse, wenn der Verein sie erfasst (aps.member_address: 'optional' | 'required')
    address: { line: 'Via Roma 1', postal_code: '20100', city: 'Milano', province: 'MI' }
});
```

Was ein Verein von einer antragstellenden Person verlangt, hängt nicht davon ab, über welchen Weg der Antrag kommt:

- **die Datenschutzhinweise.** Solange der Verein welche in Kraft hat, die die Person noch nicht gesehen hat, ist `privacy_notice`
  Pflicht: ohne sie `422 validation_failed` auf dem Feld `privacy_notice`; mit einer Version, die nicht mehr in
  Kraft ist, `422 privacy_notice_outdated`. `vimine.legal.acceptances()` sagt, was noch zu bestätigen ist; der Text ist
  `GET /v1/aps/{slug}/privacy-notice`. Die Kenntnisnahme wird als Nachweis gespeichert, an diese Mitgliedschaft gebunden.
- **die Adresse.** `aps.member_address` sagt, ob der Verein sie erfasst: Bei `required` ergibt ein Antrag ohne
  `address` die Antwort `422 validation_failed` auf dem Feld `address`; bei `none` wird sie ignoriert.
- **wer die Person ist.** Ist das Profil noch nicht identifiziert — weder Steuernummer noch Geburtsdatum, Geburtsort und
  Geburtsland —, antwortet der Antrag mit `422 identification_required`: Vervollständigen Sie das Profil mit `vimine.me.update(...)` und
  wiederholen Sie (siehe [Mitglieder ohne Steuernummer](https://developers.vimine.network/de/guides/members-without-cf/#identification_required)).

:::caution[Geändert am 2. Oktober 2026]
Bis zur Version `2026-09-27` der API verlangte dieser Antrag weder die Datenschutzhinweise noch die Adresse. Eine Integration, die
ihn ohne `privacy_notice` für einen Verein mit geltenden Datenschutzhinweisen aufruft, erhält jetzt `422`.
:::

## Ausweisprüfung

Der digitale Ausweis ist ein **signiertes Token** (`vmt_…`), das das Mitglied als QR-Code vorzeigt. Es lässt sich auf zwei Arten prüfen.

### Online

Ohne Zugangsdaten, ohne personenbezogene Daten in der Antwort:

```
GET https://api.vimine.network/v1/verify/vmt_…
→ { "status": "active", "valid_until": "2027-03-31T22:59:59.000Z" }
   oder
→ { "status": "inactive", "reason": "expired" }
   oder
→ { "status": "inactive", "reason": "not_yet_valid", "valid_from": "2026-09-19T19:00:00.000Z" }
```

`not_yet_valid`: Der Ausweis ist echt und die Mitgliedschaft aktiv, aber die Organisation hat eine Frist zwischen
Aufnahme und Nutzbarkeit des Ausweises festgelegt. `valid_from` sagt, ab wann er gilt; bis dahin ist er am Einlass ein Ausweis,
der nicht gilt. Derselbe Zeitpunkt steht im Token (`nbf`), sodass ein Offline-Scanner allein zur selben Antwort kommt.

```js
import { publicApi } from '@vimine/sdk';

const { status } = await publicApi().verifyCard(tokenReadFromQr);
```

### Offline

Das Token ist mit **Ed25519** signiert; die öffentlichen Schlüssel liegen unter
`https://api.vimine.network/.well-known/vimine-card-keys.json` (auch `GET /v1/card-keys` oder
`publicApi().cardKeys()`). Ein Scanner prüft mit dem zwischengespeicherten Schlüssel **drei Dinge**: die Signatur, das Gültigkeitsfenster (`nbf`/`exp`, höchstens 90
Tage, dann rotiert das Token) und dass der Status in den Nutzdaten aktiv ist — **`s === "active"`**. Dann synchronisiert er sich
online, sobald das Netz wieder da ist. Eine unbekannte `kid`
bedeutet, dass der Schlüssel rotiert wurde: Laden Sie die Schlüssel neu.

Das Token rotiert bei jeder Statusänderung, bei jeder Verlängerung und spätestens alle 90 Tage: Ein alter Screenshot
öffnet nichts für immer.

Einen Ausweis gibt es nur für eine **aktive** Mitgliedschaft: Solange der Antrag aussteht (Aufnahme oder Beitrag) und wenn die
Mitgliedschaft ausgesetzt oder widerrufen ist, stellt die API kein Token aus — `card` ist `null` in `GET /v1/me/membership`, und
`GET /v1/me/membership/card`, das Bild, die E-Mail und der Google-Wallet-Link antworten `409 not_active`. Die Prüfung von `s`
bleibt Pflicht: Sie weist Tokens ab, die vor dieser Regel ausgestellt wurden, und jedes Token, das keinen aktiven Status angibt.
Beachten Sie auch `nbf`: Eine Organisation kann entscheiden, dass ein Ausweis erst einige Stunden nach der Aufnahme nutzbar wird, und dieser Zeitpunkt
reist dort mit.

:::caution
Die Offline-Prüfung sagt, dass das Token beim Ausstellen **echt und innerhalb seines Fensters** war, nicht dass der Ausweis
genau in diesem Augenblick aktiv ist: Eine spätere Aussetzung zeigt sich nur online. Synchronisieren Sie, sobald Sie können.
:::

## Steuernummer

Die Steuernummer ist **optional**: Wer keine hat, tritt mit Geburtsdatum, Geburtsort und Geburtsland bei (siehe
[Mitglieder ohne Steuernummer](https://developers.vimine.network/de/guides/members-without-cf/)). Wenn es eine gibt, prüft `POST /v1/validate/fiscal-code`
(oder `publicApi().validateFiscalCode(...)`) Aufbau und Prüfzeichen eines italienischen Codice fiscale
und, wenn Sie auch die persönlichen Daten übergeben, deren Übereinstimmung mit der Steuernummer — und sagt, was die Steuernummer selbst enthält:

```js
const r = await publicApi().validateFiscalCode({ code: 'RSSMRA80A01H501U', first_name: 'Mario', last_name: 'Rossi', date: '1980-01-01', place: 'Roma', country: 'IT' });
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
//   oder
// { valid: false, error: 'mismatch', mismatches: ['date'], hints: [], birth_date: '1980-01-01', birth_country: 'IT' }
```

`error` ist `invalid` (Aufbau oder Prüfzeichen), `mismatch` (eine korrekt aufgebaute Steuernummer, die nicht zu den gesendeten Daten
passt) oder `null`; `hints` enthält `names_swapped`, wenn die Steuernummer passt, sobald Nachname und Vorname vertauscht werden. Für eine
im Ausland geborene Person senden Sie `country` (ISO 3166-1 alpha-2): Ihre Steuernummer enthält nur den Staat, und `place` — die Stadt — wird
nicht verglichen.
