VimineServer
import { VimineServer } from '@vimine/sdk/server';const vimineServer = new VimineServer({ clientId: 'vmc_…', clientSecret: process.env.VIMINE_SECRET });Per i sistemi dell’associazione: un client riservato che si autentica con client_credentials, legge lo
stato delle tessere e guida la sua sandbox. Node ≥ 18. Il secret non deve mai raggiungere un browser.
Opzioni
Sezione intitolata “Opzioni”| Opzione | Tipo | Default | |
|---|---|---|---|
clientId |
string |
— | obbligatorio |
clientSecret |
string |
— | obbligatorio; vms_… o vms_test_… |
scopes |
Scope[] |
tutti quelli registrati | un sottoinsieme da chiedere |
apiUrl |
string |
https://api.vimine.network |
|
fetch |
typeof fetch |
globalThis.fetch |
token()
Sezione intitolata “token()”Un access token del client (client_credentials, HTTP Basic), messo in cache fino a 30 secondi prima della
scadenza (1 ora); una sola richiesta anche con chiamate concorrenti.
request(path, init?)
Sezione intitolata “request(path, init?)”Una chiamata autenticata con Authorization: Bearer; JSON in entrata e in uscita; errori come
VimineError. Una risposta 401 (credenziali ruotate o revocate) viene ritentata una volta
con un token nuovo.
memberships.status(id)
Sezione intitolata “memberships.status(id)”Stato di una tessera dell’associazione, senza dati personali (memberships:read):
const s = await vimineServer.memberships.status('uuid-della-tessera');// { id, status: 'active', verdict: 'valid', valid_until: '2027-03-31T22:59:59.000Z', membership_type: { cod: 'ORD' } }signup(input, opts?)
Sezione intitolata “signup(input, opts?)”Crea un account Vimine per una persona (identity:signup): riceverà la mail per confermare l’indirizzo e
scegliere la password. Risponde sempre { status: 'accepted' } — se l’indirizzo aveva già un account non ti
viene detto (lo sa il titolare, per posta). opts.idempotencyKey (generata se assente).
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi', phone: '+39 …' });enroll(input, opts?)
Sezione intitolata “enroll(input, opts?)”Invia una richiesta di tesseramento per una persona (enroll:write): un indirizzo nuovo ottiene account e richiesta
in attesa; il titolare di un account esistente riceve invece un link di conferma (non viene scritto nulla finché
non lo usa). accept_statuto (versione in vigore) è obbligatorio; privacy_notice lo è quando
l’associazione pubblica un’informativa ai soci (association(slug).privacy_notice): la persona la legge sul tuo sito e
tu passi la versione mostrata. Vedi Integrazioni server.
La persona si identifica con codicefiscale oppure con dob + birthplace + birth_country — mai nessuno dei
due (422, voce required su ogni dato mancante). Un codice che non concorda con i dati è una voce
codicefiscale_mismatch in err.errors, con mismatches, hints e confirmable: se confirmable è true e la
persona conferma, ripeti con codicefiscale_confirmed: true. address (MemberAddress) quando l’associazione
raccoglie l’indirizzo dei soci (aps.member_address). Vedi Soci senza codice fiscale.
// con un codice fiscaleawait vimineServer.enroll({ ...persona, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'ordine-42' });// senza: data, luogo e Stato di nascitaawait vimineServer.enroll({ ...persona, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });checkins.create(token)
Sezione intitolata “checkins.create(token)”Registra un accesso dal token letto sul QR (checkins:write). Una tessera risponde { kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at } — identity_check: 'pending' è una nota da mostrare a chi è all’ingresso, non fa parte di verdict e non limita nulla (guida); il QR di un’iscrizione a un evento risponde { kind: 'registration', verdict, registration_id, event, first, membership_verdict, … } (vedi Eventi e iscrizioni).
events.list(params?) · events.get(slug) — gli eventi pubblicati dell’associazione, compresi quelli riservati ai soci (qualsiasi token del client).
registrations
Sezione intitolata “registrations”registrations.listForEvent(slug, { status?, limit?, cursor? }) |
le iscrizioni di un evento: id, stato, tipo, tessera, ingresso — senza dati personali (registrations:read, piani con gli elenchi via API) |
registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?) |
iscrive un ospite senza account a un evento pubblico; sempre { status: 'accepted' }; Idempotency-Key generata (vedi ospiti) |
memberships.list(params?)
Sezione intitolata “memberships.list(params?)”Elenco delle tessere dell’associazione (memberships:read, piani con gli elenchi): { items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }; params: status, updated_since, limit, cursor.
sandbox.members() · sandbox.reset()
Sezione intitolata “sandbox.members() · sandbox.reset()”Solo per i client di test: i soci sintetici della sandbox e il reset (nuova password
condivisa, restituita una volta). Un client di produzione riceve 403.
Gli endpoint pubblici, senza credenziali: vedi publicApi.
Un middleware di esempio
Sezione intitolata “Un middleware di esempio”import express from 'express';import { VimineServer, VimineError } from '@vimine/sdk/server';
const vimineServer = new VimineServer({ clientId: process.env.VIMINE_CLIENT_ID, clientSecret: process.env.VIMINE_SECRET });const app = express();
app.get('/tessere/:id/stato', async (req, res) => { try { const { status, verdict, valid_until } = await vimineServer.memberships.status(req.params.id); res.json({ status, verdict, valid_until }); } catch (err) { if (err instanceof VimineError && err.code === 'not_found') return res.status(404).end(); throw err; }});