Salta ai contenuti

Soci senza codice fiscale

Apri come Markdown

Per diventare socio di un’associazione non serve un codice fiscale italiano. Quello che serve è che la persona sia identificata, e su Vimine lo è in uno di due modi:

  • con un codice fiscale, oppure
  • con i suoi dati di nascita: dob + birthplace + birth_country (ISO 3166-1 alpha-2).

Mai nessuno dei due. Il resto di questa guida è che cosa cambia, campo per campo, per un’integrazione che iscrive persone — studenti in scambio, volontari arrivati da poco, soci che vivono all’estero.

POST /v1/enroll e POST /v1/signup accettano gli stessi campi:

Campo
codicefiscale facoltativo. Se c’è, viene validato e confrontato con nome, cognome, data e luogo di nascita
dob data di nascita, YYYY-MM-DD
birthplace il comune per chi è nato in Italia; la città, a testo libero, per chi è nato all’estero
birth_country Stato di nascita, due lettere (IT, FR, SN…)
gender solo statistico: non viene più confrontato con il codice
// Senza codice fiscale: data, luogo e Stato di nascita
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 }
});

Su /v1/enroll, senza codice, ogni dato che manca è un 422 validation_failed con una voce in errors[]:

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

/v1/signup crea solo l’account: lì i dati di nascita restano facoltativi, e serviranno quando la persona chiederà una tessera.

Il codice fiscale di chi è nato in Italia contiene il comune di nascita; quello di chi è nato all’estero contiene solo lo Stato. Per questo il confronto, per loro, guarda birth_country e tratta birthplace — la città — come testo libero. Inviare birth_country è ciò che fa passare una persona nata all’estero con un codice valido: senza, «Lyon» verrebbe cercata tra i comuni italiani e il codice risulterebbe incoerente. (Un modulo che finora inviava il nome dello Stato come luogo di nascita — birthplace: 'Francia' — continua a passare; birth_country è la forma da preferire.)

Lo stesso vale per il controllo pubblico, che ora dice anche che cosa il codice contiene:

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 e birth_country sono letti dal codice stesso: puoi usarli per precompilare il modulo. Questo endpoint confronta ancora gender, se lo invii; l’iscrizione e il profilo no.

Un codice scritto male (struttura o carattere di controllo) è invalid_codicefiscale: va corretto, non si può confermare. Un codice ben formato che non concorda con i dati inseriti risponde invece 422 con una voce più ricca:

{
"code": "validation_failed",
"errors": [{
"field": "codicefiscale",
"code": "codicefiscale_mismatch",
"mismatches": ["place"],
"hints": [],
"confirmable": true
}]
}
  • mismatches — che cosa non concorda: lastName, firstName, date, place;
  • hints — names_swapped quando il codice torna scambiando nome e cognome: di solito basta proporre lo scambio;
  • confirmable — se la persona può confermare che quello è il codice della sua tessera sanitaria. Succede davvero: comuni soppressi, traslitterazioni, nomi registrati in un altro ordine.

Quando confirmable è true, chiedilo alla persona e ripeti la richiesta con codicefiscale_confirmed: true. Quando nome, cognome e data non concordano tutti insieme, nulla lega più quel codice a quella persona: confirmable è false e l’unica strada è correggere.

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 });
}
}

Non inviare codicefiscale_confirmed di tua iniziativa: è una dichiarazione della persona, e viene registrata come tale.

Chi si è iscritto senza codice può aggiungerlo quando lo ottiene, dal proprio profilo:

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

PATCH /v1/me accetta birth_country e, solo in scrittura, codicefiscale_confirmed; codicefiscale si può anche riportare a null. GET /v1/me dice come la persona è identificata:

codicefiscale_check
matched un codice fiscale che concorda con i dati della persona
mismatch_confirmed un codice che non concorda, confermato dalla persona
absent nessun codice: la identificano data, luogo e Stato di nascita
null non ancora identificata: non può chiedere una tessera

matched vuol dire coerente, non verificato presso l’anagrafe tributaria: il codice fiscale resta un dato dichiarato.

Un account può esistere senza essere identificato — chi si è registrato solo per accedere, per esempio. Quando una persona così chiede una tessera, l’API lo dice e nomina che cosa manca:

  • POST /v1/me/membership → 422 identification_required, con errors[] = i dati mancanti ({ field: 'birthplace', code: 'required' }…). Manda la persona a completare il profilo con PATCH /v1/me (permesso profile:write) e ripeti la richiesta.
  • POST /v1/enroll/confirm — il link «conferma la richiesta» che riceve il titolare di un account esistente → 409 identification_required, stesso errors[]. Chiedi i dati nella pagina e invia di nuovo il token con identification:
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? } }

I dati viaggiano nel corpo della richiesta, mai nel link: nel link non c’è nulla di personale. identification completa solo i campi vuoti del profilo, non sovrascrive quelli che ci sono; se non regge, la risposta è un 422 validation_failed come sopra.

L’ammissione non cambia: pending_reasons resta quello che dice il modello di ammissione dell’associazione, per chiunque (vedi Ammissione e attivazione).

Un codice fiscale che concorda con i dati si può riscontrare; i dati di nascita da soli, o un codice confermato dalla persona, no. Per questi soci l’associazione guarda un documento una volta, la prima volta che la persona è presente. Chi integra uno scanner lo vede in POST /v1/checkins (permesso checkins:write), nelle risposte per una tessera:

const r = await vimineServer.checkins.create('vmt_…');
// { kind: 'card', verdict: 'valid', …, identity_check: 'pending' }
if (r.identity_check === 'pending') show('Primo ingresso: chiedi un documento');
identity_check
pending il socio non ha un codice fiscale verificabile e nessuno dell’associazione ha ancora visto un documento: mostralo a chi è all’ingresso
verified qualcuno dell’associazione ha visto un documento
flagged qualcuno l’ha visto e non corrispondeva: se ne occupa la segreteria
null niente da controllare

È una nota, non fa parte di verdict e non limita nulla: una tessera valid con identity_check: 'pending' entra. L’esito si registra dalla console dell’associazione, non dall’API. La verifica pubblica GET /v1/verify/{card_token} non lo riporta mai: chi controlla una tessera da fuori vede solo stato e scadenza.

Alcune associazioni tengono l’indirizzo dei soci nel proprio libro, altre no. La scelta è dell’associazione e vale per tutti i soci o per nessuno; il profilo pubblico la dichiara:

GET https://api.vimine.network/v1/aps/{slug}
→ { "aps": { …, "member_address": "optional" } }
member_address
none non chiederlo: un address inviato comunque non viene conservato
optional chiedilo, senza obbligo
required senza address l’iscrizione risponde 422 con { field: 'address', code: 'required' }
await vimineServer.enroll({
…,
address: { kind: 'residence', line: 'Via Roma 1', postal_code: '20121', city: 'Milano', province: 'MI', country: 'IT' }
});

line e city sono obbligatori dentro address; kind vale residence (predefinito) o domicile, quando è diverso dalla residenza; country è IT se omesso; province riguarda solo gli indirizzi italiani. /v1/signup non ha address: l’indirizzo appartiene all’adesione, non all’account.

Non esiste un’impostazione «codice fiscale obbligatorio». Un’associazione non può chiedere il codice fiscale a ogni persona che fa domanda: chi non ce l’ha si identifica con i dati di nascita, ovunque. Se il tuo modulo lo rende obbligatorio, è una scelta del tuo modulo — e lascia fuori persone che l’associazione potrebbe accogliere.

Tutto additivo: un modulo che invia sempre un codice fiscale continua a funzionare senza toccare nulla. Quello che un’integrazione esistente può notare:

  • codicefiscale non è più obbligatorio in POST /v1/enroll; senza codice servono dob, birthplace e birth_country (422 required su ciascuno che manca);
  • nuovi campi facoltativi birth_country, codicefiscale_confirmed (enroll, signup, PATCH /v1/me) e address (enroll);
  • gender non viene più confrontato con il codice: un 422 per il solo sesso non arriva più;
  • un codice che non concorda risponde sempre codicefiscale_mismatch, ora con hints e confirmable accanto a mismatches; un codice malformato resta invalid_codicefiscale;
  • GET/PATCH /v1/me hanno ora uno schema dichiarato (Me), con birth_country e codicefiscale_check;
  • nuovo errore identification_required: 422 su POST /v1/me/membership, 409 su POST /v1/enroll/confirm (che accetta identification nel corpo);
  • POST /v1/validate/fiscal-code accetta country e risponde anche hints, birth_date, birth_country; error vale invalid, mismatch o null;
  • POST /v1/checkins aggiunge identity_check alle risposte per una tessera; GET /v1/verify/{card_token} resta com’era;
  • il profilo pubblico aggiunge aps.member_address.