# Grid notifications for electricians, filed by a robot.

> elmeld files electricity registrations for an electrical contractor: new EV chargers and broken meter seals, reported to the grid operator. A robot works the grid operator’s portal like a person would, one address at a time, and a dashboard and a daily email show what was filed, what failed and what needs a person.

- URL: https://bon.do/en/work/elmeld
- Client: elmeld
- Sector: Electrical contracting
- Platforms: Browser robot, web dashboard and daily email
- Role: Automation platform
- Outcome: Files grid notifications by robot and hands a person only the cases that need one.
- Tags: Automation, SaaS

## Overview

elmeld files electricity registrations for an electrical contractor: new EV chargers and broken meter seals, reported to the grid operator. A robot works the grid operator’s portal like a person would, one address at a time, and a dashboard and a daily email show what was filed, what failed and what needs a person.

Every new charger and every broken meter seal has to be registered with the grid operator, in a web portal with no API. By hand that means logging in, typing the address, waiting for the address check, confirming the address belongs to that grid and filling in a form in several sections, again and again. An address that belongs to another grid operator only shows up halfway through, and a registration that silently failed is only noticed when someone goes looking.

We built an RPA platform in Node 22 and TypeScript. A Playwright robot signs in to the portal’s Azure B2C login once, then keeps the session alive with AES-256-GCM encrypted tokens and silent renewal. Each address runs seven steps: portal, registration type, address lookup against DAWA, grid check, case creation, form and submit, each one timed. The queue is PostgreSQL with SKIP LOCKED claiming and recovery of stuck jobs. Retries depend on the error code: a wrong grid operator is never retried, it is set aside and reported per grid operator, and after five failed attempts a job is handed to a person. An Angular 21 dashboard and a daily report email show the result, and the platform runs on Kubernetes.

Registrations arrive through the API, a Zapier integration or a CSV batch, and the robot files them while the team does other work. Every case shows its seven timed steps and, when it fails, a named error instead of a stack trace. Wrong-grid addresses are separated from real failures, and the daily email lists what needs follow-up. The platform has 594 automated tests across 73 files.

## Results

- **Per registration:** 7 steps. Each one timed, from the portal to submit.
- **Before a person steps in:** 5 attempts. Retries follow the error code. A wrong grid is never retried.
- **Automated:** 594 tests. Unit, integration and simulation tests in 73 files.

## At a glance

- **Client:** Anderup Ntech (elmeld)
- **Product:** RPA for grid notifications
- **Users:** Electrical contractors · office staff
- **Our role:** Design, build and operation
- **Type:** B2B automation
- **Stack:** Node 22 · TypeScript · Hono · Playwright · PostgreSQL · Angular 21 · Kubernetes
- **Timeline:** Ongoing product partnership

## What we built

### 01. A robot with a session that holds

Playwright through Azure B2C, encrypted tokens, silent renewal, a DAWA address lookup and a grid check that waits for the portal to settle.

### 02. A queue that knows its errors

PostgreSQL with SKIP LOCKED, a hard timeout, recovery of stuck jobs and retries chosen by error code, so a person only sees what a person must decide.

### 03. A dashboard and a daily report

A live queue, timing per step, a year of history on a calendar and a map, and an email every day with what was filed and what needs follow-up.

## How it ran

### 01. Discover: The portal, step by step

Login, address lookup, grid check and every form field, mapped for both registration types.

### 02. Design: A failure for every case

A named error per failure, so the queue knows what to retry, what to set aside and what to hand to a person.

### 03. Build: Tested without a browser

A hexagonal backend, 594 tests and a dry run that checks a whole batch without submitting anything.

### 04. Run: Operated, not just delivered

Kubernetes, a browser memory guard, Datadog monitors on runs and reports, and a runbook for the failures that actually happen.



## Contact

Bon.do ApS · Rantzausmindevej 228, 5700 Svendborg, Denmark · info@bon.do · https://bon.do/en/contact-us