Server integrations
For the systems of the association itself — management software, a turnstile, a CMS plugin — a
confidential client with client_id + client_secret. The secret is shown once and must never reach a
browser.
Authenticating
Section titled “Authenticating”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 — 1-hour token, renewed by itselfWithout the SDK: POST /v1/oauth/token with grant_type=client_credentials and HTTP Basic
client_id:client_secret (or client_id/client_secret in the body):
curl -u "vmc_…:vms_…" -d "grant_type=client_credentials&scope=memberships:read" \ https://api.vimine.network/v1/oauth/tokenOn the read endpoints (/v1/memberships/{id}/status, /v1/memberships) HTTP Basic is also accepted
directly, without going through a token, for hardware and low-code platforms — never from a browser.
Status of a card
Section titled “Status of a card”const s = await vimineServer.memberships.status('membership-uuid');// { id, status: 'active', verdict: 'valid', valid_until: '2027-…', membership_type: { cod: 'ORD' } }No personal data: status and validity only. The id is the one the member sees on their own card
(membership.id via Sign in with Vimine) or that your system received when it registered it.
To verify a card shown at the door use the QR token with
/v1/verify/{card_token} instead: it needs no authentication at all.
Enrol members from your system
Section titled “Enrol members from your system”With identity:signup and enroll:write your back office can create accounts and submit membership requests
without the person going through Vimine: they receive by e-mail the link to confirm the address and pick a
password.
// The account only (the person is not a member yet)await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi' });
// Account + pending membership request, approved by the secretariat in the Vimine back officeconst { 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', // optional: without it, dob + birthplace + birth_country are needed dob: '1980-01-01', birthplace: 'Roma', birth_country: 'IT', gender: 'M', // demographic only: it is not cross-checked against the code membership_type: membership_types[0].uid, // optional when there is exactly one type accept_statuto: { version: statuto.version, sha256: statuto.sha256 }, privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256 } // required when the association publishes one});The answer is always 202 { status: 'accepted' }, whatever lies behind the address: you cannot learn whether
an e-mail already has a Vimine account (only its owner is told, by mail). Specifically:
- new address → account created and, with
enroll, a pending membership request; welcome mail with the address confirmation; - address already registered → with
enrollnothing is written: the owner receives a “confirm your request to <association>” link valid 24 hours, and the request is created only when they use it. If they are already a member, nothing happens; 422errors are about your payload only (validation_failedwitherrors[].field,statuto_outdated,invalid_type);403 api_disabledwhen the association does not accept requests;403 not_availableduring the private beta, while accounts are created by invitation only.
A person is identified by a fiscal code or by their date, place and country of birth (dob, birthplace,
birth_country) — never neither: without a code, each missing particular is a required entry in errors[]. For a
person born abroad always send birth_country: their code holds the state only. A code that disagrees with the
particulars answers 422 with a codicefiscale_mismatch entry carrying mismatches, hints and confirmable: when
confirmable is true the person may confirm it, and you repeat the call with codicefiscale_confirmed: true. When
the association collects its members’ address (aps.member_address in the public profile) add address. When it
collects the data of whoever holds parental responsibility (aps.collects_guardian_data) and the person is a minor by
dob, guardian is required — first_name, last_name, relation, optionally codicefiscale, email, phone —
else 422 with guardian: required. The whole path is in
Members without a fiscal code.
accept_statuto must name the statuto in force: show it to the person on your site (it is in
association(slug); the text and the document in GET /v1/aps/{slug}/statuto) and pass the
version they accepted. An association that has registered no readable statuto answers accepting_requests: false, and
the request 403 not_available. The same goes for the association’s
privacy notice to members (privacy_notice in association(slug), the text at GET /v1/aps/{slug}/privacy-notice):
when the association publishes one, the request must name the version the person read, else 422 (privacy_notice
missing or privacy_notice_outdated); the acknowledgement is recorded in the person’s name, your client named.
Optional, same rule: accept_terms and acknowledge_privacy (the account terms and the platform’s privacy notice,
GET /v1/legal/{kind}) when the person accepted them on your site. The calls are idempotent: the SDK
generates an Idempotency-Key per call, or pass your own ({ idempotencyKey: 'order-42' }) to retry safely
after a timeout. Mails are budgeted (3 per address per day) and never sent from the sandbox.
Door check-ins
Section titled “Door check-ins”With checkins:write a device at the entrance records a member’s passage by sending the token read from the
card’s QR (not an identifier): the answer is the verdict alone, no personal data. HTTP Basic is accepted
directly, for hardware and low-code platforms.
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 }curl -u "vmc_…:vms_…" -H "Content-Type: application/json" -d '{"token":"vmt_…"}' https://api.vimine.network/v1/checkinsThe body is { "token": "vmt_…" } — the token read from the QR, of a card or of an event registration; for cards only, card is accepted as an alias.
A malformed or forged token is a 422 invalid_card; another association’s card is “not found”.
identity_check: 'pending' is a note for whoever staffs the gate: the member has no verifiable fiscal code and nobody
of the association has looked at a document yet. Show it; it is not part of verdict and it restricts nothing (see
the identity check).
Listing memberships (Pro and Network plans)
Section titled “Listing memberships (Pro and Network plans)”vimineServer.memberships.list({ status?, updated_since?, limit?, cursor? }) returns ids, status and dates — never
personal data — in pages (next_cursor). With updated_since you sync only what changed. On plans that do
not include it the answer is 403 plan_limit.
Secret rotation
Section titled “Secret rotation”From the console (Rotate secret): the new secret is shown once, the previous one keeps working for 24 hours, so you can update your systems without downtime. If you suspect a compromise, revoke the client: all of its sessions end immediately.
Security
Section titled “Security”- A wrong secret, repeated, locks the client out for 15 minutes.
- Every token is bound to the client’s association: a card of another association is simply “not found”.
- A
401with just-rotated credentials is retried once by the SDK with a fresh token. - Responses use
application/problem+json(see Permissions, errors and limits).