# Integrazioni server

> Client riservati per i sistemi dell'associazione, client_credentials, stato delle tessere, rotazione del secret.

URL: https://developers.vimine.network/guides/server/

Per i sistemi **dell'associazione stessa** — gestionale, tornello, plugin del CMS — un client **riservato**
con `client_id` + `client_secret`. Il secret è mostrato una sola volta e non deve mai raggiungere un browser.

## Autenticarsi

```js title="Node"
import { VimineServer } from '@vimine/sdk/server';

const vimineServer = new VimineServer({
    clientId: 'vmc_…',
    clientSecret: process.env.VIMINE_SECRET,
    scopes: ['memberships:read']
});
const token = await vimineServer.token();      // client_credentials — token di 1 ora, rinnovato da solo
```

Senza SDK: `POST /v1/oauth/token` con `grant_type=client_credentials` e HTTP Basic `client_id:client_secret`
(o `client_id`/`client_secret` nel corpo):

```sh
curl -u "vmc_…:vms_…" -d "grant_type=client_credentials&scope=memberships:read" \
     https://api.vimine.network/v1/oauth/token
```

Sugli endpoint di lettura (`/v1/memberships/{id}/status`, `/v1/memberships`) è accettato anche HTTP Basic
**direttamente**, senza passare dal token, per hardware e piattaforme low-code — mai da un browser.

## Stato di una tessera

```js
const s = await vimineServer.memberships.status('uuid-della-tessera');
// { id, status: 'active', verdict: 'valid', valid_until: '2027-…', membership_type: { cod: 'ORD' } }
```

Nessun dato personale: solo stato e validità. L'`id` è quello che il socio vede nella propria tessera
(`membership.id` via *Accedi con Vimine*) o che il tuo sistema ha ricevuto quando l'ha registrata.

Per verificare una tessera **mostrata allo scanner** usa invece il token del QR con
[`/v1/verify/{card_token}`](https://developers.vimine.network/guides/embed-and-verify/): non serve nemmeno autenticarsi.

## Registrare soci dal tuo sistema

Con `identity:signup` e `enroll:write` il tuo gestionale può creare account e inviare richieste di tesseramento
**senza che la persona passi da Vimine**: riceverà per e-mail il link per confermare l'indirizzo e scegliere la
password.

```js
// Solo l'account (la persona non è ancora socia)
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi' });

// Account + richiesta di tessera in attesa, che la segreteria approva dal gestionale Vimine
const { statuto, membership_types } = await vimineServer.public.association('tuo-slug');
await vimineServer.enroll({
    email: 'mario.rossi@example.org',
    first_name: 'Mario',
    last_name: 'Rossi',
    codicefiscale: 'RSSMRA80A01H501U',      // facoltativo: senza, servono dob + birthplace + birth_country
    dob: '1980-01-01', birthplace: 'Roma', birth_country: 'IT',
    gender: 'M',                              // solo statistico: non viene confrontato con il codice
    membership_type: membership_types[0].uid, // facoltativo se il tipo è uno solo
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 },
    privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256 } // obbligatorio se l'associazione ne pubblica una
});
```

La risposta è **sempre** `202 { status: 'accepted' }`, qualunque cosa ci sia dietro l'indirizzo: non puoi sapere
se un'e-mail ha già un account Vimine (lo sa solo il suo titolare, per posta). In particolare:

- **indirizzo nuovo** → account creato e, con `enroll`, richiesta di tessera *in attesa*; mail di benvenuto con la
  conferma dell'indirizzo;
- **indirizzo già registrato** → con `enroll` non viene scritto nulla: il titolare riceve un link «conferma la
  richiesta a *\<associazione\>*» valido 24 ore, e la richiesta nasce solo quando lo usa. Se è già socio, non
  succede niente;
- errori `422` riguardano solo il *tuo* payload (`validation_failed` con `errors[].field`, `statuto_outdated`,
  `invalid_type`); `403 api_disabled` se l'associazione non accetta richieste; `403 not_available` durante la
  [beta privata](https://developers.vimine.network/guides/scopes-and-errors/#la-beta-privata), quando gli account nascono solo su invito.

Una persona si identifica con il **codice fiscale oppure** con data, luogo e Stato di nascita (`dob`, `birthplace`,
`birth_country`) — mai nessuno dei due: senza codice, ogni dato mancante è una voce `required` in `errors[]`. Per chi
è nato all'estero invia sempre `birth_country`: il suo codice contiene solo lo Stato. Un codice che non concorda con
i dati risponde `422` con una voce `codicefiscale_mismatch` che porta `mismatches`, `hints` e `confirmable`: quando
`confirmable` è `true` la persona può confermarlo, e tu ripeti la chiamata con `codicefiscale_confirmed: true`. Se
l'associazione raccoglie l'indirizzo dei soci (`aps.member_address` nel profilo pubblico) aggiungi `address`. Se
raccoglie i dati di chi esercita la responsabilità genitoriale (`aps.collects_guardian_data`) e la persona è minorenne
per `dob`, `guardian` è obbligatorio — `first_name`, `last_name`, `relation`, e facoltativi `codicefiscale`, `email`,
`phone` — altrimenti `422` con `guardian: required`. Tutto il percorso è in
[Soci senza codice fiscale](https://developers.vimine.network/guides/members-without-cf/).

`accept_statuto` deve indicare lo statuto **in vigore**: mostralo alla persona sul tuo sito (lo trovi in
[`association(slug)`](https://developers.vimine.network/sdk/public/); il testo e il documento in `GET /v1/aps/{slug}/statuto`) e passa la versione che
ha accettato. Un'associazione che non ha registrato uno statuto leggibile risponde `accepting_requests: false` e la
richiesta `403 not_available`. Lo stesso vale per l'**informativa privacy
ai soci** (`privacy_notice` in `association(slug)`, il testo in `GET /v1/aps/{slug}/privacy-notice`): quando
l'associazione ne pubblica una la richiesta deve indicarne la versione letta, altrimenti `422` (`privacy_notice`
mancante o `privacy_notice_outdated`); la presa visione viene registrata a nome della persona, con il tuo client
indicato. Facoltativi ma con la stessa regola: `accept_terms` e `acknowledge_privacy` (le condizioni dell'account e
l'informativa della piattaforma, `GET /v1/legal/{kind}`), se la persona le ha accettate sul tuo sito. Le chiamate sono idempotenti: l'SDK
genera una `Idempotency-Key` per ogni chiamata, oppure passane una tua (`{ idempotencyKey: 'ordine-42' }`) per
ripetere in sicurezza dopo un timeout. Le mail sono contingentate (3 al giorno per indirizzo) e mai inviate dalla
sandbox.

## Accessi da tornello o scanner

Con `checkins:write` un dispositivo agli ingressi registra il passaggio di un socio inviando **il token letto dal
QR** della tessera (non un identificativo): la risposta è solo il verdetto, senza dati personali. HTTP Basic è
accettato direttamente, per hardware e piattaforme low-code.

```js
const r = await vimineServer.checkins.create('vmt_…');   // { verdict: 'valid' | 'expired' | 'not_yet_valid' | 'statuto_required' | 'inactive', status, membership_id, valid_until, identity_check, checked_in_at }
```

```sh
curl -u "vmc_…:vms_…" -H "Content-Type: application/json" -d '{"token":"vmt_…"}' https://api.vimine.network/v1/checkins
```

Il corpo è `{ "token": "vmt_…" }` — il token letto dal QR, di una tessera o dell'iscrizione a un evento; per le sole tessere `card` è accettato come sinonimo.

Un token malformato o contraffatto è un `422 invalid_card`; una tessera di un'altra associazione è «non trovata».

`identity_check: 'pending'` è una nota per chi è all'ingresso: il socio non ha un codice fiscale verificabile e nessuno
dell'associazione ha ancora visto un documento. Mostrala; non fa parte di `verdict` e non limita nulla (vedi
[il controllo d'identità](https://developers.vimine.network/guides/members-without-cf/#allingresso-il-controllo-didentità)).

## Elenco delle tessere (piani Pro e Network)

`vimineServer.memberships.list({ status?, updated_since?, limit?, cursor? })` restituisce identificativi, stato e date —
**mai dati personali** — a pagine (`next_cursor`). Con `updated_since` sincronizzi solo ciò che è cambiato. Sui
piani che non lo includono la risposta è `403 plan_limit`.

## Rotazione del secret

Dalla console (*Rigenera secret*): il nuovo secret è mostrato una volta, **il precedente continua a funzionare
per 24 ore**, così puoi aggiornare i tuoi sistemi senza interruzioni. Se sospetti una compromissione, revoca il
client: tutte le sue sessioni terminano subito.

## Sicurezza

- Un secret sbagliato più volte blocca il client per 15 minuti.
- Ogni token è legato all'associazione del client: una tessera di un'altra associazione è semplicemente
  «non trovata».
- Una risposta `401` con credenziali appena ruotate viene ritentata una volta dall'SDK con un token nuovo.
- Le risposte usano `application/problem+json` (vedi [Permessi, errori e limiti](https://developers.vimine.network/guides/scopes-and-errors/)).
