# Intégrations serveur

> Les clients confidentiels pour les systèmes de l’association, client_credentials, l’état des cartes, la rotation du secret.

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

Pour les systèmes **de l’association elle-même** — logiciel de gestion, tourniquet, plugin de CMS — un client
**confidentiel** avec `client_id` + `client_secret`. Le secret n’est affiché qu’une fois et ne doit jamais atteindre
un navigateur.

## S’authentifier

```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 — jeton d’une heure, renouvelé de lui-même
```

Sans le SDK : `POST /v1/oauth/token` avec `grant_type=client_credentials` et HTTP Basic
`client_id:client_secret` (ou `client_id`/`client_secret` dans le corps) :

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

Sur les endpoints de lecture (`/v1/memberships/{id}/status`, `/v1/memberships`), HTTP Basic est aussi accepté
**directement**, sans passer par un jeton, pour le matériel et les plateformes low-code — jamais depuis un navigateur.

## État d’une carte

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

Aucune donnée personnelle : l’état et la validité seulement. L’`id` est celui que le membre voit sur sa propre carte
(`membership.id` via *Se connecter avec Vimine*) ou que votre système a reçu en l’enregistrant.

Pour vérifier une carte **présentée à l’entrée**, utilisez plutôt le jeton du QR avec
[`/v1/verify/{card_token}`](https://developers.vimine.network/fr/guides/embed-and-verify/) : il ne demande aucune authentification.

## Faire adhérer des membres depuis votre système

Avec `identity:signup` et `enroll:write`, votre back-office peut créer des comptes et envoyer des demandes d’adhésion
**sans que la personne passe par Vimine** : elle reçoit par e-mail le lien pour confirmer son adresse et choisir un
mot de passe.

```js
// Le compte seulement (la personne n’est pas encore membre)
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi' });

// Compte + demande d’adhésion en attente, approuvée par le secrétariat dans le back-office Vimine
const { statuto, membership_types } = await vimineServer.public.association('your-slug');
await vimineServer.enroll({
    email: 'mario.rossi@example.org',
    first_name: 'Mario',
    last_name: 'Rossi',
    codicefiscale: 'RSSMRA80A01H501U',      // facultatif : sans lui, il faut dob + birthplace + birth_country
    dob: '1980-01-01', birthplace: 'Roma', birth_country: 'IT',
    gender: 'M',                              // démographique seulement : il n’est pas comparé avec le code
    membership_type: membership_types[0].uid, // facultatif quand il y a exactement un type
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 },
    privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256 } // obligatoire quand l’association en publie une
});
```

La réponse est **toujours** `202 { status: 'accepted' }`, quoi qu’il y ait derrière l’adresse : vous ne pouvez pas
savoir si un e-mail a déjà un compte Vimine (seul son titulaire en est informé, par e-mail). Concrètement :

- **nouvelle adresse** → compte créé et, avec `enroll`, une demande d’adhésion *en attente* ; e-mail de bienvenue avec
  la confirmation de l’adresse ;
- **adresse déjà enregistrée** → avec `enroll`, rien n’est écrit : le titulaire reçoit un lien « confirmez votre
  demande à *\<association\>* » valable 24 heures, et la demande n’est créée que lorsqu’il l’utilise. S’il est déjà
  membre, il ne se passe rien ;
- les erreurs `422` ne concernent que *votre* payload (`validation_failed` avec `errors[].field`, `statuto_outdated`,
  `invalid_type`) ; `403 api_disabled` quand l’association n’accepte pas de demandes ; `403 not_available` pendant la
  [bêta privée](https://developers.vimine.network/fr/guides/scopes-and-errors/#la-bêta-privée), tant que les comptes ne sont créés que sur invitation.

Une personne est identifiée par un **code fiscal ou** par sa date, son lieu et son pays de naissance (`dob`,
`birthplace`, `birth_country`) — jamais ni l’un ni l’autre : sans code, chaque donnée manquante est une entrée
`required` dans `errors[]`. Pour une personne née à l’étranger, envoyez toujours `birth_country` : son code ne contient
que l’État. Un code en désaccord avec les données de naissance répond `422` avec une entrée `codicefiscale_mismatch`
qui porte `mismatches`, `hints` et `confirmable` : quand `confirmable` vaut `true`, la personne peut le confirmer, et
vous répétez l’appel avec `codicefiscale_confirmed: true`. Quand l’association recueille l’adresse de ses membres
(`aps.member_address` dans le profil public), ajoutez `address`. Quand elle recueille les données de la personne
qui exerce l’autorité parentale (`aps.collects_guardian_data`) et que la personne est mineure d’après `dob`,
`guardian` est obligatoire — `first_name`, `last_name`, `relation`, et facultativement `codicefiscale`, `email`,
`phone` — sinon `422` avec `guardian: required`. Le parcours complet est dans
[Membres sans code fiscal](https://developers.vimine.network/fr/guides/members-without-cf/).

`accept_statuto` doit nommer le statuto **en vigueur** : montrez-le à la personne sur votre site (il est dans
[`association(slug)`](https://developers.vimine.network/fr/sdk/public/) ; le texte et le document dans `GET /v1/aps/{slug}/statuto`) et passez la
version qu’elle a acceptée. Une association qui n’a enregistré aucun statuto lisible répond
`accepting_requests: false`, et la demande `403 not_available`. Il en va de même pour la **notice d’information sur
la vie privée destinée aux membres** de l’association (`privacy_notice` dans `association(slug)`, le texte sur
`GET /v1/aps/{slug}/privacy-notice`) : quand l’association en publie une, la demande doit nommer la version que la
personne a lue, sinon `422` (`privacy_notice` manquante ou `privacy_notice_outdated`) ; la prise de connaissance est
enregistrée au nom de la personne, en nommant votre client. Facultatifs, même règle : `accept_terms` et
`acknowledge_privacy` (les conditions du compte et la notice d’information de la plateforme, `GET /v1/legal/{kind}`)
quand la personne les a acceptées sur votre site. Les appels sont idempotents : le SDK génère une `Idempotency-Key`
par appel, ou passez la vôtre (`{ idempotencyKey: 'order-42' }`) pour réessayer sans risque après un délai dépassé.
Les e-mails sont plafonnés (3 par adresse et par jour) et ne sont jamais envoyés depuis la sandbox.

## Contrôles à l’entrée

Avec `checkins:write`, un appareil à l’entrée enregistre le passage d’un membre en envoyant **le jeton lu sur le QR de
la carte** (pas un identifiant) : la réponse est le verdict seul, sans données personnelles. HTTP Basic est accepté
directement, pour le matériel et les plateformes 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
```

Le corps est `{ "token": "vmt_…" }` — le jeton lu sur le QR, d’une carte ou d’une inscription à un événement ; pour
les cartes seulement, `card` est accepté comme alias.

Un jeton malformé ou falsifié donne `422 invalid_card` ; la carte d’une autre association est « introuvable ».

`identity_check: 'pending'` est une indication pour la personne qui tient l’entrée : le membre n’a pas de code fiscal
vérifiable et personne de l’association n’a encore regardé une pièce d’identité. Affichez-la ; elle ne fait pas partie
de `verdict` et ne restreint rien (voir [le contrôle d’identité](https://developers.vimine.network/fr/guides/members-without-cf/#à-lentrée-le-contrôle-didentité)).

## Lister les adhésions (formules Pro et Network)

`vimineServer.memberships.list({ status?, updated_since?, limit?, cursor? })` renvoie les identifiants, l’état et les
dates — **jamais de données personnelles** — par pages (`next_cursor`). Avec `updated_since`, vous ne synchronisez que
ce qui a changé. Sur les formules qui ne l’incluent pas, la réponse est `403 plan_limit`.

## Rotation du secret

Depuis la console (*Régénérer le secret*) : le nouveau secret n’est affiché qu’une fois, **l’ancien continue de
fonctionner pendant 24 heures**, pour que vous puissiez mettre à jour vos systèmes sans interruption. Si vous
soupçonnez une compromission, révoquez le client : toutes ses sessions prennent fin immédiatement.

## Sécurité

- Un secret erroné, répété, bloque le client pendant 15 minutes.
- Chaque jeton est lié à l’association du client : une carte d’une autre association est simplement « introuvable ».
- Un `401` avec des identifiants tout juste régénérés est réessayé une fois par le SDK avec un nouveau jeton.
- Les réponses sont en `application/problem+json` (voir [Autorisations, erreurs et limites](https://developers.vimine.network/fr/guides/scopes-and-errors/)).
