Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

beid : Belgian ID card holders

The lino_xl.lib.beid plugin adds functionality for reading Belgian eID cards and storing that data in the database.

Lines starting with >>> in this document are code snippets that get tested as part of our development workflow.

>>> import lino
>>> lino.startup('lino_book.projects.avanti1.settings')
>>> from lino.api.doctest import *

See also unit tests in lino_welfare.tests.test_beid.


Installing this package makes sense only if there is exactly one subclass of the BeIdCardHolder model mixin among your application’s models. That model is referrable as lino_xl.lib.beid.Plugin.holder_model.

>>> dd.plugins.beid.holder_model
<class 'lino_avanti.lib.avanti.models.Client'>

See unit tests in lino_welfare.tests.test_beid.

class lino_xl.lib.beid.SSIN

A mixin that adds two fields national_id and nationality.

You can use this mixin also when the plugin is not installed.

Class attributes:

validate_national_id = False

Whether to validate the national_id immediately before saving a record. If this is False, the national_id might contain invalid values which would later be reported as data problem messages.


The SSIN. It is a nullable char field declared unique. It is not validated directly because that would cause problems with legacy data where SSINs need manual control. See also BeIdCardHolderChecker.

Database fields:


The nationality. This is a pointer to countries.Country which should contain also entries for refugee statuses.

Note that the nationality is not being read from eID card because it is stored there as a language and gender specific plain text.

class lino_xl.lib.beid.BeIdCardHolder

Mixin for models which represent an eid card holder. Currently only Belgian eid cards are tested. Concrete subclasses must also inherit from lino.mixins.Born.

Database fields:


The nationality in plain text as it was on the eid card. For a French citizen the value on the eid card can be “Français”, “Française”, “Frans”, “Franzose”, “Französin”, “French” depending on the sex and the issuing municipality. This field is just informative, Lino does not automatically map it to the nationality field.


The birth place in plain text as it was on the eid card.


The type of id card.

Choices are defined in BeIdCardTypes


The administration who issued this ID card.


The eventual noble condition of this person.


Update card holder data from eID card

Read eId card and store the data on the selected holder.

This is a row action (called on a given holder).

When the selected holder has an empty national_id, and when there is no holder yet with that national_id in the database, then we want to update the existing holder from the card.


Find or create card holder from eID card

Read an eID card, then either show the existing holder or ask to create a new holder.

This is a list action, usually called from a quicklink or a main menu item.


Virtual field which displays the picture.

ID card types

>>> show_choicelist(beid.BeIdCardTypes)
======= ================== ======================================== ==================================================================================== ==============================================
 value   name               en                                       de                                                                                   fr
------- ------------------ ---------------------------------------- ------------------------------------------------------------------------------------ ----------------------------------------------
 01      belgian_citizen    Belgian citizen                          Belgischer Staatsbürger                                                              Citoyen belge
 06      kids_card          Kids card (< 12 year)                    Kind unter 12 Jahren                                                                 Kids card (< 12 year)
 11      foreigner_a        Foreigner card A                         A (Bescheinigung der Eintragung im Ausländerregister - Vorübergehender Aufenthalt)   Étranger A (Séjour temporaire)
 12      foreigner_b        Foreigner card B                         B (Bescheinigung der Eintragung im Ausländerregister)                                Foreigner card B
 13      foreigner_c        Foreigner card C                         C (Personalausweis für Ausländer)                                                    Foreigner card C
 14      foreigner_d        Foreigner card D                         D (Daueraufenthalt - EG)                                                             Foreigner card D
 15      foreigner_e        Foreigner card E                         E (Anmeldebescheinigung)                                                             Foreigner card E
 16      foreigner_e_plus   Foreigner card E+                        E+                                                                                   Foreigner card E+
 17      foreigner_f        Foreigner card F                         F (Aufenthaltskarte für Familienangehörige eines Unionsbürgers)                      Foreigner card F
 18      foreigner_f_plus   Foreigner card F+                        F+                                                                                   Foreigner card F+
 99      orange_card        Registration certificate (Orange card)   Eintragungsbescheinigung (Orange Karte)                                              Attestation d’immatriculation (Carte orange)
======= ================== ======================================== ==================================================================================== ==============================================
class lino_xl.lib.beid.BeIdCardTypes

A list of Belgian identity card types.

We didn’t yet find any official reference document.

The eID applet returns a field documentType which contains a numeric code. For example 1 is for “Belgian citizen”, 6 for “Kids card”,…

The eID viewer, when saving a card as xml file, doesn’t save these values nowhere, it saves a string equivalent (1 becomes “belgian_citizen”, 6 becomes “kids_card”, 17 becomes “foreigner_f”, 16 becomes “foreigner_e_plus”,…


Excerpts from [1]:

  • Johan: A document type of 7 is used for bootstrap cards – What is a bootstrap card (maybe some kind of test card?) Danny: A bootstrap card was an eID card that was used in the early start of the eID card introduction to bootstrap the computers at the administration. This type is no longer issued.

  • Johan: A document type of 8 is used for a “habilitation/machtigings” card – Is this for refugees or asylum seekers? Danny: A habilitation/machtigings card was aimed at civil servants. This type is also no longer used.

Residence types

>>> rt.show(beid.ResidenceTypes)
======= ====== ========================
 value   name   text
------- ------ ------------------------
 1              Register of citizens
 2              Register of foreigners
 3              Waiting register
======= ====== ========================
class lino_xl.lib.beid.ResidenceTypes

The list of Belgian resident registers (Einwohnerregister, Registre de résidents).






Registre de la population



Registre des étrangers



Registre d’attente

class lino_xl.lib.beid.BeIdCardHolderChecker

Invalid NISSes are not refused à priori using a ValidationError (see BeIdCardHolder.national_id), but this checker reports them.

Belgian NISSes are stored including the formatting characters (see lino.utils.ssin) in order to guarantee uniqueness.


The national_id field of a client. It nullable and unique: it can be empty, but must be empty when it isn’t.

>>> fld = rt.models.avanti.Client._meta.get_field('national_id')
>>> print(fld.help_text)  
The SSIN. It is a nullable char field declared unique. It is not validated
directly because that would cause problems with legacy data where SSINs need
manual control. See also BeIdCardHolderChecker.
>>> print(fld.null)
>>> print(fld.unique)
>>> from lino_xl.lib.beid.views import read_card_data_from_file
>>> from pathlib import Path
>>> fn = "tests/beid/20190311.json"
>>> data = read_card_data_from_file(fn)
>>> print(data.success)
>>> print(data.issuing_municipality)
>>> img_file = Path(rt.models.avanti.Client.card_number_to_image_path(data.card_number))  
>>> str(img_file).endswith("/beid/592400976752.jpg")
>>> img_dir = Path(settings.MEDIA_ROOT) / 'beid'
>>> str(img_file).startswith(str(img_dir))
>>> settings.SITE.makedirs_if_missing(img_dir)
>>> kwargs = rt.models.avanti.Client.find_by_beid.card2client(data)
>>> img_file.unlink()
>>> # img_dir.rmdir()
>>> fn = "tests/beid/nocard.json"
>>> data = read_card_data_from_file(fn)
Traceback (most recent call last):
Warning: No card data found: Could not find any reader with a card inserted
>>> # skip because under Python 2 it is another exception
>>> data = read_card_data_from_file("foo")  
Traceback (most recent call last):
FileNotFoundError: [Errno 2] No such file or directory: 'foo'
class lino_xl.lib.beid.SSINChecker

A data checker that reports invalid SSINs.

See ssin : Belgian national identification numbers.