# Mitglieder ohne Steuernummer

> Eine Person wird über eine Steuernummer oder über Geburtsdatum, Geburtsort und Geburtsland identifiziert. Was für im Ausland Geborene zu senden ist, wie eine abweichende Steuernummer bestätigt wird, was am Einlass zu zeigen ist.

URL: https://developers.vimine.network/de/guides/members-without-cf/

Für den Beitritt zu einem Verein braucht es **keine italienische Steuernummer** (codice fiscale). Es braucht, dass die Person
identifiziert ist, und auf Vimine ist sie das auf eine von zwei Arten:

- über eine **Steuernummer** oder
- über ihre **Geburtsdaten**: `dob` + `birthplace` + `birth_country` (ISO 3166-1 alpha-2).

Nie über keins von beidem. Der Rest dieser Anleitung beschreibt, was sich Feld für Feld für eine Integration ändert, die Menschen aufnimmt —
Austauschstudierende, gerade angekommene Freiwillige, Mitglieder, die im Ausland leben.

## Eine Person identifizieren

`POST /v1/enroll` und `POST /v1/signup` nehmen dieselben Felder:

| Feld | |
|---|---|
| `codicefiscale` | optional. Wenn vorhanden, wird sie geprüft und mit den Namen, dem Geburtsdatum und dem Geburtsort abgeglichen |
| `dob` | Geburtsdatum, `YYYY-MM-DD` |
| `birthplace` | die Gemeinde (*comune*) bei einer in Italien geborenen Person; die Stadt, als freier Text, bei einer im Ausland geborenen Person |
| `birth_country` | Geburtsland, zwei Buchstaben (`IT`, `FR`, `SN`…) |
| `gender` | nur statistisch: wird **nicht mehr** mit der Steuernummer abgeglichen |

```js
// Ohne Steuernummer: Geburtsdatum, Geburtsort und Geburtsland
await vimineServer.enroll({
    email: 'marie.dupont@example.org',
    first_name: 'Marie',
    last_name: 'Dupont',
    dob: '1985-03-10',
    birthplace: 'Lyon',
    birth_country: 'FR',
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 }
});
```

Bei `/v1/enroll` ohne Steuernummer ist jede fehlende Geburtsangabe ein `422 validation_failed` mit einem Eintrag in `errors[]`:

```json
{ "code": "validation_failed", "errors": [{ "field": "birth_country", "code": "required" }] }
```

`/v1/signup` legt nur das Konto an: Dort bleiben die Geburtsdaten optional; nötig werden sie, wenn die Person eine
Mitgliedschaft beantragt.

## Im Ausland geboren: `birth_country` senden

Die Steuernummer einer in Italien geborenen Person enthält die Geburtsgemeinde; die einer im Ausland geborenen Person enthält **nur den
Staat**. Deshalb vergleicht der Abgleich bei ihnen `birth_country` und behandelt `birthplace` — die Stadt — als freien Text.
Erst `birth_country` lässt eine im Ausland geborene Person mit gültiger Steuernummer durchgehen: Ohne das Feld würde „Lyon“
unter den italienischen Gemeinden gesucht, und die Steuernummer erschiene widersprüchlich. (Ein Formular, das bisher den italienischen Namen des
Staates als Geburtsort sendete — `birthplace: 'Francia'` —, geht weiterhin durch; `birth_country` ist die bevorzugte Form.)

Dasselbe gilt für die öffentliche Prüfung, die jetzt auch sagt, was die Steuernummer enthält:

```js
const r = await publicApi().validateFiscalCode({
    code: 'DPNMRA85C50Z110U', first_name: 'Marie', last_name: 'Dupont', date: '1985-03-10', place: 'Lyon', country: 'FR'
});
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1985-03-10', birth_country: 'FR' }
```

`birth_date` und `birth_country` werden aus der Steuernummer selbst gelesen: Sie können damit das Formular vorausfüllen. Dieser Endpunkt
vergleicht `gender` weiterhin, wenn Sie es senden; Beitritt und Profil tun es nicht.

## Eine abweichende Steuernummer und die Bestätigung

Eine falsch eingegebene Steuernummer (Aufbau oder Prüfzeichen) ist `invalid_codicefiscale`: Sie muss korrigiert werden und kann nie
bestätigt werden. Eine korrekt aufgebaute Steuernummer, die **nicht** zu den eingegebenen Daten passt, antwortet dagegen mit `422` und einem ausführlicheren Eintrag:

```json
{
  "code": "validation_failed",
  "errors": [{
    "field": "codicefiscale",
    "code": "codicefiscale_mismatch",
    "mismatches": ["place"],
    "hints": [],
    "confirmable": true
  }]
}
```

- `mismatches` — was nicht passt: `lastName`, `firstName`, `date`, `place`;
- `hints` — `names_swapped`, wenn die Steuernummer passt, sobald Nachname und Vorname vertauscht werden: Den Tausch anzubieten genügt
  meistens;
- `confirmable` — ob die Person **bestätigen** darf, dass dies die Steuernummer auf ihrer Gesundheitskarte ist. Das kommt vor:
  Gemeinden, die es nicht mehr gibt, Transliterationen, Namen, die in anderer Reihenfolge eingetragen sind.

Ist `confirmable` gleich `true`, fragen Sie die Person und wiederholen den Antrag mit `codicefiscale_confirmed: true`. Wenn beide
Namen **und** das Datum nicht passen, verbindet nichts mehr diese Steuernummer mit dieser Person: `confirmable` ist `false`, und der
einzige Weg ist, sie zu korrigieren.

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

try {
    await vimineServer.enroll(input);
} catch (err) {
    const entry = err instanceof VimineError && err.errors?.find((e) => e.code === 'codicefiscale_mismatch');
    if (!entry) throw err;
    if (entry.confirmable && (await askThePerson(entry.mismatches, entry.hints))) {
        await vimineServer.enroll({ ...input, codicefiscale_confirmed: true });
    }
}
```

Senden Sie `codicefiscale_confirmed` nicht von sich aus: Es ist eine Erklärung der Person und wird als solche
erfasst.

## Die Steuernummer später ergänzen

Wer ohne Steuernummer beigetreten ist, kann sie in seinem eigenen Profil ergänzen, sobald er eine hat:

```js
const me = await vimine.me.update({ codicefiscale: 'DPNMRA85C50Z110U' });
me.codicefiscale_check;   // 'matched'
```

`PATCH /v1/me` akzeptiert `birth_country` und das nur schreibbare `codicefiscale_confirmed`; `codicefiscale` kann auch wieder
auf `null` gesetzt werden. `GET /v1/me` sagt, wie die Person identifiziert ist:

| `codicefiscale_check` | |
|---|---|
| `matched` | eine Steuernummer, die zu den Geburtsdaten der Person passt |
| `mismatch_confirmed` | eine abweichende Steuernummer, von der Person bestätigt |
| `absent` | keine Steuernummer: Geburtsdatum, Geburtsort und Geburtsland identifizieren sie |
| `null` | noch nicht identifiziert: Sie kann keine Mitgliedschaft beantragen |

`matched` bedeutet **stimmig**, nicht beim Steuerregister geprüft: Eine Steuernummer bleibt eine erklärte Angabe.

## `identification_required`

Ein Konto kann existieren, ohne identifiziert zu sein — etwa wenn sich jemand nur registriert hat, um sich anzumelden. Wenn eine solche
Person eine Mitgliedschaft beantragt, sagt die API es und nennt, was fehlt:

- **`POST /v1/me/membership`** → `422 identification_required`, mit `errors[]` = die fehlenden Geburtsangaben
  (`{ field: 'birthplace', code: 'required' }`…). Schicken Sie die Person zur Vervollständigung ihres Profils mit `PATCH /v1/me`
  (Berechtigung `profile:write`) und wiederholen Sie den Antrag.
- **`POST /v1/enroll/confirm`** — der Link „Bestätigen Sie Ihren Antrag“, den der Inhaber eines bestehenden Kontos erhält →
  `409 identification_required`, dieselben `errors[]`. Fragen Sie die Geburtsdaten auf der Seite ab und senden Sie das Token erneut mit
  `identification`:

```js
await publicApi().confirmEnrollment(token, { dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR' });
// POST /v1/enroll/confirm  { token, identification: { dob, birthplace, birth_country, codicefiscale?, codicefiscale_confirmed? } }
```

Die Geburtsdaten reisen **im Body der Anfrage, nie im Link**: Im Link steht nie etwas Persönliches.
`identification` füllt nur die leeren Felder des Profils, es überschreibt nichts, was schon da ist; wenn es nicht
stimmig ist, lautet die Antwort `422 validation_failed` wie oben.

Die Aufnahme ändert sich nicht: Die `pending_reasons` sind, was das Aufnahmemodell des Vereins vorsieht, für alle (siehe
[Aufnahme und Aktivierung](https://developers.vimine.network/de/guides/admission/)).

## Am Einlass: die Identitätsprüfung

Eine Steuernummer, die zu den Geburtsdaten passt, lässt sich mit ihnen abgleichen; die Geburtsdaten allein oder eine Steuernummer, die die
Person bestätigt hat, nicht. Bei diesen Mitgliedern sieht sich der Verein **einmal** ein Ausweisdokument an, beim ersten Besuch der Person.
Wer einen Scanner integriert, sieht das in `POST /v1/checkins` (Berechtigung `checkins:write`), in den Antworten
für einen Ausweis:

```js
const r = await vimineServer.checkins.create('vmt_…');
// { kind: 'card', verdict: 'valid', …, identity_check: 'pending' }
if (r.identity_check === 'pending') show('Erster Einlass: nach einem Ausweisdokument fragen');
```

| `identity_check` | |
|---|---|
| `pending` | das Mitglied hat keine prüfbare Steuernummer, und noch niemand aus dem Verein hat ein Ausweisdokument angesehen: **Zeigen Sie es** der Person am Einlass |
| `verified` | jemand aus dem Verein hat ein Ausweisdokument angesehen |
| `flagged` | jemand hat eines angesehen, und es passte nicht: Das Sekretariat des Vereins kümmert sich darum |
| `null` | nichts zu prüfen |

Es ist ein **Hinweis**, nicht Teil von `verdict`, und er **schränkt nichts ein**: Ein `valid`-Ausweis mit
`identity_check: 'pending'` kommt herein. Das Ergebnis wird in der Konsole des Vereins erfasst, nicht über die API. Die
öffentliche Prüfung `GET /v1/verify/{card_token}` trägt ihn nie: Wer einen Ausweis von außen prüft, sieht nur Status und
Ablauf.

## Die Adresse

Manche Vereine führen die Adresse ihrer Mitglieder in ihrem Mitgliederbuch, andere nicht. Die Wahl trifft der Verein, und sie
gilt **für alle Mitglieder oder für keines**; das öffentliche Profil erklärt sie:

```
GET https://api.vimine.network/v1/aps/{slug}
→ { "aps": { …, "member_address": "optional" } }
```

| `member_address` | |
|---|---|
| `none` | nicht fragen: Eine trotzdem gesendete `address` wird nicht gespeichert |
| `optional` | fragen, ohne sie zur Pflicht zu machen |
| `required` | ohne `address` antwortet der Beitritt mit `422` und `{ field: 'address', code: 'required' }` |

```js
await vimineServer.enroll({
    …,
    address: { kind: 'residence', line: 'Via Roma 1', postal_code: '20121', city: 'Milano', province: 'MI', country: 'IT' }
});
```

`line` und `city` sind innerhalb von `address` Pflicht; `kind` ist `residence` (Standard) oder `domicile`, wenn es sich
vom Wohnsitz unterscheidet; `country` ist `IT`, wenn es fehlt; `province` betrifft nur italienische Adressen. `/v1/signup` hat keine
`address`: Die Adresse gehört zur Mitgliedschaft, nicht zum Konto.

## Ein Verein kann die Steuernummer nicht verlangen

Es gibt keine Einstellung „Steuernummer Pflicht“. Ein Verein kann nicht von allen Antragstellenden eine Steuernummer verlangen: Wer
keine hat, wird überall über seine Geburtsdaten identifiziert. Wenn Ihr Formular sie zur Pflicht macht, ist das eine Entscheidung Ihres
Formulars — und es schließt Menschen aus, die der Verein willkommen heißen könnte.

## Was sich geändert hat (Version 2026-09-19)

Alles additiv: **Ein Formular, das immer eine Steuernummer sendet, funktioniert unverändert weiter**. Was eine bestehende Integration
bemerken kann:

- `codicefiscale` ist in `POST /v1/enroll` nicht mehr Pflicht; ohne Steuernummer sind `dob`, `birthplace` und `birth_country`
  nötig (`422` `required` für jedes fehlende Feld);
- neue optionale Felder `birth_country`, `codicefiscale_confirmed` (enroll, signup, `PATCH /v1/me`) und `address`
  (enroll);
- `gender` wird nicht mehr mit der Steuernummer abgeglichen: Ein `422` allein wegen des Geschlechts kommt nicht mehr vor;
- eine abweichende Steuernummer antwortet weiterhin mit `codicefiscale_mismatch`, jetzt mit `hints` und `confirmable` neben
  `mismatches`; eine fehlerhaft aufgebaute Steuernummer bleibt `invalid_codicefiscale`;
- `GET`/`PATCH /v1/me` haben jetzt ein deklariertes Schema (`Me`), mit `birth_country` und `codicefiscale_check`;
- ein neuer Fehler `identification_required`: `422` bei `POST /v1/me/membership`, `409` bei `POST /v1/enroll/confirm` (das
  `identification` im Body akzeptiert);
- `POST /v1/validate/fiscal-code` akzeptiert `country` und antwortet zusätzlich mit `hints`, `birth_date`, `birth_country`; `error`
  ist `invalid`, `mismatch` oder `null`;
- `POST /v1/checkins` fügt den Antworten für einen Ausweis `identity_check` hinzu; `GET /v1/verify/{card_token}` bleibt, wie es war;
- das öffentliche Profil ergänzt `aps.member_address`.
