# Integraciones de servidor

> Clientes confidenciales para los sistemas de la propia asociación, client_credentials, estado de los carnés, rotación del secreto.

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

Para los sistemas **de la propia asociación** — un programa de gestión, un torniquete, un plugin de CMS — un cliente
**confidencial** con `client_id` + `client_secret`. El secreto se muestra una sola vez y nunca debe llegar a un
navegador.

## Autenticarse

```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 de 1 hora, que se renueva solo
```

Sin el SDK: `POST /v1/oauth/token` con `grant_type=client_credentials` y HTTP Basic `client_id:client_secret` (o
`client_id`/`client_secret` en el cuerpo):

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

En los endpoints de lectura (`/v1/memberships/{id}/status`, `/v1/memberships`) también se acepta HTTP Basic
**directamente**, sin pasar por un token, para hardware y plataformas low-code — nunca desde un navegador.

## Estado de un carné

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

Sin datos personales: solo el estado y la validez. El `id` es el que el socio ve en su propio carné
(`membership.id` mediante *Iniciar sesión con Vimine*) o el que tu sistema recibió cuando lo registró.

Para verificar un carné **mostrado en la entrada**, usa en cambio el token del QR con
[`/v1/verify/{card_token}`](https://developers.vimine.network/es/guides/embed-and-verify/): no necesita ninguna autenticación.

## Inscribir socios desde tu sistema

Con `identity:signup` y `enroll:write`, tu back office puede crear cuentas y enviar solicitudes de inscripción **sin
que la persona pase por Vimine**: recibe por correo el enlace para confirmar su dirección y elegir una contraseña.

```js
// Solo la cuenta (la persona todavía no es socia)
await vimineServer.signup({ email: 'mario.rossi@example.org', first_name: 'Mario', last_name: 'Rossi' });

// Cuenta + solicitud de inscripción pendiente, que la secretaría aprueba en el back office de 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',      // opcional: sin él, hacen falta dob + birthplace + birth_country
    dob: '1980-01-01', birthplace: 'Roma', birth_country: 'IT',
    gender: 'M',                              // solo demográfico: no se coteja con el código
    membership_type: membership_types[0].uid, // opcional cuando hay exactamente un tipo
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 },
    privacy_notice: privacy_notice && { version: privacy_notice.version, sha256: privacy_notice.sha256 } // obligatorio cuando la asociación publica una
});
```

La respuesta es **siempre** `202 { status: 'accepted' }`, haya lo que haya detrás de la dirección: no puedes saber si
un correo ya tiene una cuenta de Vimine (solo se le avisa a su titular, por correo). En concreto:

- **dirección nueva** → cuenta creada y, con `enroll`, una solicitud de inscripción *pendiente*; correo de bienvenida
  con la confirmación de la dirección;
- **dirección ya registrada** → con `enroll` no se escribe nada: el titular recibe un enlace «confirma tu solicitud a
  *<asociación>*» válido 24 horas, y la solicitud solo se crea cuando lo usa. Si ya es socio, no pasa nada;
- los errores `422` se refieren solo a *tu* payload (`validation_failed` con `errors[].field`, `statuto_outdated`,
  `invalid_type`); `403 api_disabled` cuando la asociación no acepta solicitudes; `403 not_available` durante la
  [beta privada](https://developers.vimine.network/es/guides/scopes-and-errors/#la-beta-privada), mientras las cuentas solo se crean por invitación.

Una persona se identifica con un **código fiscal o** con su fecha, lugar y país de nacimiento (`dob`, `birthplace`,
`birth_country`) — nunca sin ninguno de los dos: sin código, cada dato de nacimiento que falte es una entrada
`required` en `errors[]`. Para una persona nacida en el extranjero envía siempre `birth_country`: su código solo
contiene el país. Un código que no coincide con los datos de nacimiento responde `422` con una entrada
`codicefiscale_mismatch` que lleva `mismatches`, `hints` y `confirmable`: cuando `confirmable` es `true`, la persona
puede confirmarlo, y repites la llamada con `codicefiscale_confirmed: true`. Cuando la asociación recoge la dirección
de sus socios (`aps.member_address` en el perfil público), añade `address`. Cuando recoge los datos de quien ejerce la
responsabilidad parental (`aps.collects_guardian_data`) y la persona es menor según `dob`, `guardian` es obligatorio —
`first_name`, `last_name`, `relation` y, opcionalmente, `codicefiscale`, `email`, `phone` —; si no, `422` con
`guardian: required`. El recorrido completo está en [Socios sin código fiscal](https://developers.vimine.network/es/guides/members-without-cf/).

`accept_statuto` debe nombrar el estatuto **en vigor**: muéstraselo a la persona en tu sitio (está en
[`association(slug)`](https://developers.vimine.network/es/sdk/public/); el texto y el documento, en `GET /v1/aps/{slug}/statuto`) y pasa la versión
que ha aceptado. Una asociación que no ha registrado ningún estatuto legible responde `accepting_requests: false`, y
la solicitud `403 not_available`. Lo mismo vale para la **política de privacidad de la asociación para sus socios**
(`privacy_notice` en `association(slug)`, el texto en `GET /v1/aps/{slug}/privacy-notice`): cuando la asociación
publica una, la solicitud debe nombrar la versión que la persona ha leído; si no, `422` (`privacy_notice` ausente o
`privacy_notice_outdated`); el acuse de recibo se registra a nombre de la persona, con tu cliente indicado.
Opcionales, con la misma regla: `accept_terms` y `acknowledge_privacy` (las condiciones de la cuenta y la política de
privacidad de la plataforma, `GET /v1/legal/{kind}`) cuando la persona las ha aceptado en tu sitio. Las llamadas son
idempotentes: el SDK genera una `Idempotency-Key` por llamada, o pasa la tuya (`{ idempotencyKey: 'order-42' }`)
para repetir sin riesgos después de un timeout. Los correos tienen un límite (3 por dirección y día) y nunca se envían
desde el sandbox.

## Check-in en la entrada

Con `checkins:write`, un dispositivo en la entrada registra el paso de un socio enviando **el token leído del QR del
carné** (no un identificador): la respuesta es solo el veredicto, sin datos personales. Se acepta HTTP Basic
directamente, para hardware y plataformas 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
```

El cuerpo es `{ "token": "vmt_…" }` — el token leído del QR, de un carné o de una inscripción a un evento; solo para
los carnés, `card` se acepta como alias.

Un token mal formado o falsificado es un `422 invalid_card`; un carné de otra asociación es «no encontrado».

`identity_check: 'pending'` es una nota para quien atiende la entrada: el socio no tiene un código fiscal comprobable
y nadie de la asociación ha mirado todavía un documento. Muéstrala; no forma parte de `verdict` y no restringe nada
(consulta [la comprobación de identidad](https://developers.vimine.network/es/guides/members-without-cf/#en-la-entrada-la-comprobación-de-identidad)).

## Listar las inscripciones (planes Pro y Network)

`vimineServer.memberships.list({ status?, updated_since?, limit?, cursor? })` devuelve ids, estados y fechas —
**nunca datos personales** — por páginas (`next_cursor`). Con `updated_since` sincronizas solo lo que ha cambiado. En
los planes que no lo incluyen, la respuesta es `403 plan_limit`.

## Rotación del secreto

Desde la consola (*Regenerar secreto*): el secreto nuevo se muestra una sola vez y **el anterior sigue funcionando
durante 24 horas**, para que puedas actualizar tus sistemas sin interrupciones. Si sospechas que está comprometido,
revoca el cliente: todas sus sesiones terminan de inmediato.

## Seguridad

- Un secreto erróneo, repetido, bloquea el cliente durante 15 minutos.
- Cada token está vinculado a la asociación del cliente: un carné de otra asociación es simplemente «no encontrado».
- El SDK repite una vez, con un token nuevo, un `401` recibido con credenciales recién regeneradas.
- Las respuestas usan `application/problem+json` (consulta [Permisos, errores y límites](https://developers.vimine.network/es/guides/scopes-and-errors/)).
