Reverse-Engineering the Norwegian Police Booking API

2022-06-20

Abstract

This article outlines the process of reverse-engineering the network requests used by the Norwegian police booking system to create a simplified interface for retrieving available appointment times for passports and ID cards. A lightweight client was implemented to expose only the essential functionality, providing fast access to availability data while avoiding the complexity of the official booking flow. The interface is publicly accessible at app.eipi.dev/pass-og-id.

Introduction

This small application polls the public booking interface for passports and ID cards at the Norwegian police. The mechanism relies on careful querying of the underlying API exposed at pass-og-id.politiet.no, which provides structured information about districts, offices, services and available dates. The site itself remains intentionally sparse: a quiet shelf for things worth keeping.

Methods

All requests originate from a lightweight PHP backend that executes authenticated-but-public endpoints from the Qmatic booking system. Both districts and offices are harvested into a local SQLite database to support faster lookups. Available appointment dates are queried by combining branch identifiers with the appropriate service public ID for either passports or ID cards.

Results

The API offers predictable JSON structures for both hierarchical branch listings and daily availability windows. By crawling forward a chosen number of days (dager), the backend returns either the earliest free appointment or a complete listing of all available dates. The resulting data are displayed through a minimal HTML interface.

Discussion

The system behaves consistently as long as the upstream service maintains its schema. Occasional rate limits may appear, though infrequently. Because the API exposes all district and office metadata, the application can stay self-updating without manual corrections. The structure lends itself well to further expansion, such as caching, historical tracking of availability, or predictive modelling of appointment load.

Appendix

Two tables are used:

The dager parameter controls the look-ahead window for availability queries.

<?php
$dager = $_GET['dager'];
$date = date('Y-m-d');
$PublicId =
'd1b043c75655a6756852ba9892255243c08688a071e3b58b64c892524f58d098';

$begynn_link = "https://pass-og-id.politiet.no/
  qmaticwebbooking/rest/schedule/branches/";
$slutt_link = ";servicePublicId=" . $PublicId . ";customSlotLength=10";

$db = new SQLite3('itWorks.db');
$db->exec("CREATE TABLE distrikt(id INTEGER PRIMARY KEY,
  distrikt_id TEXT, distrikt_navn TEXT)");
$db->exec("CREATE TABLE avdeling(id INTEGER PRIMARY KEY,
  distrikt_id TEXT, distrikt_navn TEXT,
  avdeling_navn TEXT, avdeling_id TEXT)");

$data = file_get_contents('https://pass-og-id.politiet.no/
  qmaticwebbooking/rest/schedule/branchGroups;
  servicePublicId=' . $PublicId);

$decoded = json_decode($data, true);

foreach ($decoded as $branch) {
    $distrikt_navn = $branch['name'];
    $distrikt_id = $branch['id'];
    $db->exec("INSERT INTO distrikt(distrikt_id, distrikt_navn)
      VALUES('$distrikt_id', '$distrikt_navn')");

    foreach ($branch['branches'] as $avd) {
        $avdeling_navn = $avd['name'];
        $avdeling_id = $avd['id'];
        $db->exec("INSERT INTO avdeling(distrikt_id, distrikt_navn,
          avdeling_navn, avdeling_id)
          VALUES('$distrikt_id', '$distrikt_navn',
          '$avdeling_navn', '$avdeling_id')");
    }
}
?>

References

Pass-og-ID Interface. (2025). Appointment availability viewer. Retrieved from https://app.eipi.dev/pass-og-id




Home | Privacy Policy