# Associados sem código fiscal

> Uma pessoa é identificada por um código fiscal ou pela data, local e país de nascimento. O que enviar para quem nasceu no exterior, como se confirma um código que não confere, o que mostrar na entrada.

URL: https://developers.vimine.network/pt/guides/members-without-cf/

Associar-se a uma associação **não exige um código fiscal italiano** (codice fiscale). O que exige é que a pessoa
esteja identificada, e no Vimine ela está de uma de duas formas:

- por um **código fiscal**, ou
- pelos seus **dados de nascimento**: `dob` + `birthplace` + `birth_country` (ISO 3166-1 alpha-2).

Nunca por nenhum dos dois. O resto deste guia é o que muda, campo por campo, para uma integração que faz a adesão de
pessoas — estudantes de intercâmbio, voluntários recém-chegados, associados que moram no exterior.

## Identificar uma pessoa

`POST /v1/enroll` e `POST /v1/signup` recebem os mesmos campos:

| Campo | |
|---|---|
| `codicefiscale` | opcional. Quando presente, é validado e conferido com os nomes, a data e o local de nascimento |
| `dob` | data de nascimento, `YYYY-MM-DD` |
| `birthplace` | o *comune* (o município italiano) para quem nasceu na Itália; a cidade, em texto livre, para quem nasceu no exterior |
| `birth_country` | país de nascimento, duas letras (`IT`, `FR`, `SN`…) |
| `gender` | só estatístico: **não é mais conferido** com o código |

```js
// Sem código fiscal: data, local e país de nascimento
await vimineServer.enroll({
    email: 'marie.dupont@example.org',
    first_name: 'Marie',
    last_name: 'Dupont',
    dob: '1985-03-10',
    birthplace: 'Lyon',
    birth_country: 'FR',
    accept_statuto: { version: statuto.version, sha256: statuto.sha256 }
});
```

Em `/v1/enroll`, sem um código, cada dado de nascimento que falta é um `422 validation_failed` com uma entrada em `errors[]`:

```json
{ "code": "validation_failed", "errors": [{ "field": "birth_country", "code": "required" }] }
```

`/v1/signup` cria só a conta: ali os dados de nascimento continuam opcionais, e serão necessários quando a pessoa pedir
uma carteirinha.

## Nascidos no exterior: envie `birth_country`

O código fiscal de quem nasceu na Itália traz o comune de nascimento; o de quem nasceu no exterior traz **só o
país**. Por isso, para essas pessoas, a conferência compara `birth_country` e trata `birthplace` — a cidade — como texto
livre. Enviar `birth_country` é o que faz passar uma pessoa nascida no exterior com um código válido: sem ele, "Lyon"
seria procurada entre os comuni italianos e o código sairia incoerente. (Um formulário que até agora enviava o nome
italiano do país como local de nascimento — `birthplace: 'Francia'` — continua passando; `birth_country` é a forma
preferível.)

O mesmo vale para a verificação pública, que agora também diz o que o código traz:

```js
const r = await publicApi().validateFiscalCode({
    code: 'DPNMRA85C50Z110U', first_name: 'Marie', last_name: 'Dupont', date: '1985-03-10', place: 'Lyon', country: 'FR'
});
// { valid: true, error: null, mismatches: [], hints: [], birth_date: '1985-03-10', birth_country: 'FR' }
```

`birth_date` e `birth_country` são lidos do próprio código: você pode usá-los para preencher o formulário. Este
endpoint ainda compara `gender`, quando você o envia; a adesão e o perfil, não.

## Um código que não confere, e a confirmação

Um código digitado errado (estrutura ou caractere de controle) é `invalid_codicefiscale`: ele precisa ser corrigido,
nunca pode ser confirmado. Um código bem formado que **não confere** com os dados informados responde, em vez disso,
`422` com uma entrada mais rica:

```json
{
  "code": "validation_failed",
  "errors": [{
    "field": "codicefiscale",
    "code": "codicefiscale_mismatch",
    "mismatches": ["place"],
    "hints": [],
    "confirmable": true
  }]
}
```

- `mismatches` — o que não confere: `lastName`, `firstName`, `date`, `place`;
- `hints` — `names_swapped` quando o código confere trocando sobrenome e nome: oferecer a troca costuma
  bastar;
- `confirmable` — se a pessoa pode **confirmar** que esse é o código do seu cartão de saúde italiano. Acontece:
  comuni que não existem mais, transliterações, nomes registrados em outra ordem.

Quando `confirmable` é `true`, pergunte à pessoa e repita o pedido com `codicefiscale_confirmed: true`. Quando os dois
nomes **e** a data não conferem, nada mais liga aquele código àquela pessoa: `confirmable` é `false` e o único
caminho é corrigi-lo.

```js
import { VimineError } from '@vimine/sdk/server';

try {
    await vimineServer.enroll(input);
} catch (err) {
    const entry = err instanceof VimineError && err.errors?.find((e) => e.code === 'codicefiscale_mismatch');
    if (!entry) throw err;
    if (entry.confirmable && (await askThePerson(entry.mismatches, entry.hints))) {
        await vimineServer.enroll({ ...input, codicefiscale_confirmed: true });
    }
}
```

Não envie `codicefiscale_confirmed` por iniciativa própria: é uma declaração da pessoa, e é registrada como tal.

## Adicionar o código depois

Quem se associou sem um código pode adicioná-lo quando tiver um, pelo próprio perfil:

```js
const me = await vimine.me.update({ codicefiscale: 'DPNMRA85C50Z110U' });
me.codicefiscale_check;   // 'matched'
```

`PATCH /v1/me` aceita `birth_country` e `codicefiscale_confirmed`, só de escrita; `codicefiscale` também pode voltar a
`null`. `GET /v1/me` diz como a pessoa está identificada:

| `codicefiscale_check` | |
|---|---|
| `matched` | um código fiscal que confere com os dados de nascimento da pessoa |
| `mismatch_confirmed` | um código que não confere, confirmado pela pessoa |
| `absent` | nenhum código: data, local e país de nascimento a identificam |
| `null` | ainda não identificada: não pode pedir uma carteirinha |

`matched` quer dizer **coerente**, não verificado no cadastro tributário: um código fiscal continua sendo um dado
declarado.

## `identification_required`

Uma conta pode existir sem estar identificada — alguém que se cadastrou só para entrar, por exemplo. Quando essa
pessoa pede uma carteirinha, a API diz isso e indica o que falta:

- **`POST /v1/me/membership`** → `422 identification_required`, com `errors[]` = os dados de nascimento que faltam
  (`{ field: 'birthplace', code: 'required' }`…). Mande a pessoa completar o perfil com `PATCH /v1/me`
  (permissão `profile:write`) e repita o pedido.
- **`POST /v1/enroll/confirm`** — o link "confirme o seu pedido" que o dono de uma conta existente recebe →
  `409 identification_required`, mesmos `errors[]`. Peça os dados de nascimento na página e envie o token de novo com
  `identification`:

```js
await publicApi().confirmEnrollment(token, { dob: '1985-03-10', birthplace: 'Lyon', birth_country: 'FR' });
// POST /v1/enroll/confirm  { token, identification: { dob, birthplace, birth_country, codicefiscale?, codicefiscale_confirmed? } }
```

Os dados de nascimento viajam **no corpo da requisição, nunca no link**: nada pessoal vai no link.
`identification` só completa os campos vazios do perfil, não sobrescreve o que já existe; se os dados não conferirem, a
resposta é um `422 validation_failed` como acima.

A admissão não muda: os `pending_reasons` são os que o modelo de admissão da associação determina, para todos (veja
[Admissão e ativação](https://developers.vimine.network/pt/guides/admission/)).

## Na entrada: a verificação de identidade

Um código fiscal que confere com os dados de nascimento pode ser verificado com eles; os dados de nascimento sozinhos,
ou um código que a pessoa confirmou, não. Para esses associados a associação olha um documento **uma vez**, na
primeira vez que a pessoa vem. Quem integra um leitor vê isso em `POST /v1/checkins` (permissão `checkins:write`), nas
respostas para uma carteirinha:

```js
const r = await vimineServer.checkins.create('vmt_…');
// { kind: 'card', verdict: 'valid', …, identity_check: 'pending' }
if (r.identity_check === 'pending') show('Primeira entrada: peça um documento');
```

| `identity_check` | |
|---|---|
| `pending` | o associado não tem um código fiscal verificável e ninguém da associação olhou um documento dele ainda: **mostre isso** a quem cuida da entrada |
| `verified` | alguém da associação olhou um documento |
| `flagged` | alguém olhou um e ele não conferia: a secretaria da associação assume a partir daí |
| `null` | nada a verificar |

É uma **observação**, não faz parte do `verdict` e **não restringe nada**: uma carteirinha `valid` com
`identity_check: 'pending'` entra. O resultado é registrado pelo console da associação, não pela API. A
verificação pública `GET /v1/verify/{card_token}` nunca o traz: quem verifica uma carteirinha de fora vê só status e
vencimento.

## O endereço

Algumas associações guardam o endereço dos seus associados no livro de associados, outras não. A escolha é da
associação e vale para **todos os associados ou para nenhum**; o perfil público a declara:

```
GET https://api.vimine.network/v1/aps/{slug}
→ { "aps": { …, "member_address": "optional" } }
```

| `member_address` | |
|---|---|
| `none` | não pergunte: um `address` enviado mesmo assim não é guardado |
| `optional` | pergunte, sem torná-lo obrigatório |
| `required` | sem um `address`, a adesão responde `422` com `{ field: 'address', code: 'required' }` |

```js
await vimineServer.enroll({
    …,
    address: { kind: 'residence', line: 'Via Roma 1', postal_code: '20121', city: 'Milano', province: 'MI', country: 'IT' }
});
```

`line` e `city` são obrigatórios dentro de `address`; `kind` é `residence` (o padrão) ou `domicile`, quando difere
da residência; `country` é `IT` quando omitido; `province` diz respeito só aos endereços italianos. `/v1/signup` não tem
`address`: o endereço pertence à carteirinha, não à conta.

## Uma associação não pode exigir o código

Não existe uma configuração "código fiscal obrigatório". Uma associação não pode exigir um código fiscal de todos os que
pedem a adesão: quem não tem é identificado pelos dados de nascimento, em todo lugar. Se o seu formulário o torna
obrigatório, essa é uma escolha do seu formulário — e ela deixa de fora pessoas que a associação poderia receber.

## O que mudou (versão 2026-09-19)

Tudo aditivo: **um formulário que sempre envia um código fiscal continua funcionando sem mudanças**. O que uma
integração existente pode notar:

- `codicefiscale` não é mais obrigatório em `POST /v1/enroll`; sem um código, são necessários `dob`, `birthplace` e
  `birth_country` (`422` `required` em cada um que falta);
- novos campos opcionais `birth_country`, `codicefiscale_confirmed` (enroll, signup, `PATCH /v1/me`) e `address`
  (enroll);
- `gender` não é mais conferido com o código: um `422` só sobre o sexo não acontece mais;
- um código que não confere continua respondendo `codicefiscale_mismatch`, agora com `hints` e `confirmable` ao lado de
  `mismatches`; um código malformado continua sendo `invalid_codicefiscale`;
- `GET`/`PATCH /v1/me` agora têm um schema declarado (`Me`), com `birth_country` e `codicefiscale_check`;
- um novo erro `identification_required`: `422` em `POST /v1/me/membership`, `409` em `POST /v1/enroll/confirm` (que
  aceita `identification` no corpo);
- `POST /v1/validate/fiscal-code` aceita `country` e também responde `hints`, `birth_date`, `birth_country`; `error`
  é `invalid`, `mismatch` ou `null`;
- `POST /v1/checkins` adiciona `identity_check` às respostas para uma carteirinha; `GET /v1/verify/{card_token}` continua
  como estava;
- o perfil público adiciona `aps.member_address`.
