# VimineServer

> Reference of the VimineServer class of @vimine/sdk/server for confidential clients.

URL: https://developers.vimine.network/en/sdk/server/

```js
import { VimineServer } from '@vimine/sdk/server';
const vimineServer = new VimineServer({ clientId: 'vmc_…', clientSecret: process.env.VIMINE_SECRET });
```

For the association's **own** systems: a confidential client that authenticates with `client_credentials`,
reads card statuses and drives its sandbox. Node ≥ 18. **The secret must never reach a browser.**

## Options

| Option | Type | Default | |
|---|---|---|---|
| `clientId` | `string` | — | required |
| `clientSecret` | `string` | — | required; `vms_…` or `vms_test_…` |
| `scopes` | `Scope[]` | all the registered ones | a subset to ask for |
| `apiUrl` | `string` | `https://api.vimine.network` | |
| `fetch` | `typeof fetch` | `globalThis.fetch` | |

## Methods

### `token()`

A client access token (`client_credentials`, HTTP Basic), cached until 30 seconds before it expires (1 hour);
a single request even under concurrent calls.

### `request(path, init?)`

An authenticated call with `Authorization: Bearer`; JSON in and out; errors as
[`VimineError`](https://developers.vimine.network/en/sdk/errors/). A `401` (rotated or revoked credentials) is retried once with a fresh token.

### `memberships.status(id)`

Status of one membership of the association, without personal data (`memberships:read`):

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

### `signup(input, opts?)`

Creates a Vimine account for a person (`identity:signup`): they receive the mail to confirm the address and pick
a password. Always answers `{ status: 'accepted' }` — whether the address already had an account is not disclosed
to you (its owner is told, by mail). `opts.idempotencyKey` (generated when absent).

```js
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi', phone: '+39 …' });
```

### `enroll(input, opts?)`

Submits a membership request for a person (`enroll:write`): a new address gets an account and a *pending* request;
the owner of an existing account receives a confirmation link instead (nothing is written until they use it).
`accept_statuto` (the in-force version) is required; so is `privacy_notice` when the association
publishes a notice to its members (`association(slug).privacy_notice`): the person reads it on your site and you pass
the version shown. See [Server integrations](https://developers.vimine.network/en/guides/server/#enrol-members-from-your-system).

The person is identified by `codicefiscale` **or** by `dob` + `birthplace` + `birth_country` — never neither (`422`,
a `required` entry on each missing particular). A code that disagrees with the particulars is a
`codicefiscale_mismatch` entry in `err.errors`, with `mismatches`, `hints` and `confirmable`: when `confirmable` is
`true` and the person confirms, repeat with `codicefiscale_confirmed: true`. `address` (`MemberAddress`) when the
association collects its members' address (`aps.member_address`). See
[Members without a fiscal code](https://developers.vimine.network/en/guides/members-without-cf/).

```js
// with a fiscal code
await vimineServer.enroll({ ...person, codicefiscale: 'RSSMRA80A01H501U', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }, { idempotencyKey: 'order-42' });
// without one: date, place and country of birth
await vimineServer.enroll({ ...person, dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR', accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });
```

### `checkins.create(token)`

Records a door check-in from the token read on the QR (`checkins:write`). A card answers `{ kind: 'card', verdict, status, membership_id, valid_until, valid_from, identity_check, checked_in_at }` — `identity_check: 'pending'` is a note to show to whoever staffs the gate, it is not part of `verdict` and it restricts nothing ([guide](https://developers.vimine.network/en/guides/members-without-cf/#at-the-gate-the-identity-check)); an event registration's QR answers `{ kind: 'registration', verdict, registration_id, event, first, membership_verdict, … }` (see [Events and registrations](https://developers.vimine.network/en/guides/events/#at-the-door)).

### `events`

`events.list(params?)` · `events.get(slug)` — the association's published events, members-only ones included (any client token).

### `registrations`

| | |
|---|---|
| `registrations.listForEvent(slug, { status?, limit?, cursor? })` | an event's registrations: id, status, type, membership, check-in — no personal data (`registrations:read`, plans with API lists) |
| `registrations.guest(slug, { email, first_name, last_name, registration_type, note? }, opts?)` | registers a **guest** without an account for a public event; always `{ status: 'accepted' }`; `Idempotency-Key` generated (see [guests](https://developers.vimine.network/en/guides/events/#guests-public-events)) |

### `memberships.list(params?)`

The association's memberships (`memberships:read`, plans with lists): `{ items: [{ id, status, verdict, membership_type, issued_at, valid_until, updated_at }], next_cursor }`; `params`: `status`, `updated_since`, `limit`, `cursor`.

### `sandbox.members()` · `sandbox.reset()`

Test clients only: the synthetic members of the [sandbox](https://developers.vimine.network/en/guides/sandbox/) and the reset (new shared
password, returned once). A production client gets `403`.

### `public`

The public, credential-less endpoints: see [`publicApi`](https://developers.vimine.network/en/sdk/public/).

## An example middleware

```js title="express.js"
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('/cards/:id/status', 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;
    }
});
```
