Skip to content
Back to projects

Work2025

AutoInit and Streamline for Invoices customization at ITESOFT

I built AutoInit, an n8n workflow that fully automates client platform setup: about 2 hours of manual work across five tools replaced by a one-minute form, adopted by the whole team. Alongside, customization and production support of Streamline for Invoices for large enterprises.

Duration
September 2025 to present (work-study)
Team
Streamline for Invoices Delivery team (32 people)
My role
Assistant development & project engineer: creator of AutoInit, customization, support and production diagnosis
Technologiesn8nDockerAzure DevOpsNode.jsJavaAngularJSPostgreSQLSSH / SFTPGrafanaGit
AutoInit and Streamline for Invoices customization at ITESOFT

Context

ITESOFT is a French software company specializing in business process automation and digitization. Its Streamline for Invoices solution automates supplier invoice processing for large enterprises, from capture to ERP integration, including matching with purchase orders and approval workflows.

I have been a work-study student in the Delivery team since September 2025, first during the final year of my BUT in Computer Science (DACS track), then as part of my DevOps engineering degree at Polytech Montpellier. The team's job: adapt the standard product to each client's needs, deploy it and support it.

The context is demanding. France's e-invoicing reform, effective September 1, 2026, multiplies deployment projects, and the company is ISO 27001 certified: named access to client environments, audited SSH bastion, least-privilege service accounts. Everything I built had to fit within that framework.

This work is confidential. I can't show the code or screens or go into more detail here, but I'm happy to discuss it in an interview.

Objectives

My work-study revolves around two projects:

  1. 01AutoInit, automating platform setup: creating a new client platform took about two hours of manual steps across five tools (Git repository from the template, branches and config files, build pipeline, installation on the virtual machine, deployment, web front end, sharing access), each step a chance for error. The goal, which I set myself: reduce all of it to filling in a form.
  1. 02Streamline for Invoices customization and support: fit each large client's business rules into a standard product without breaking compatibility with its updates, and diagnose production incidents.

Technical approach

AutoInit: an end-to-end n8n workflow. The project started as a personal initiative, after doing several setups by hand. With no specification, I wrote the requirements myself: a form (client, DNS alias, environment, modules), then creating and configuring the client's Git repository, building the release, installing and deploying it on the virtual machine, exposing it on the web front end, and finally notifying the team with the access details.

I chose n8n, self-hosted in Docker, over plain scripts: it provides a web form, a visual history of every run and notifications. The workflow chains about ten steps. On the Azure DevOps side it mirrors the team's process: a dedicated branch and merge requests merged by a service account, keeping the history and safeguards of the git flow. Because the client VM and the Azure DevOps server cannot reach each other, n8n also acts as the bridge: it downloads the build artifact through the API and pushes it over SFTP through the bastion.

Reliability-driven choices. SSH connections use a library (node-ssh) called from code nodes, because the user changes with each client. Steps run sequentially on purpose: about ten minutes slower, but stable. And the workflow doesn't trust the install scripts' exit codes: it checks the platform's real state by counting the running Docker containers.

Streamline for Invoices customization. Each client project is a Git repository adding a layer on top of a standard template: JavaScript business rules run by a Node.js rules engine, Java rules on the backend, SQL scripts, SFTP connectors and cron jobs. Example: an invoice could be approved without its mandatory analytical axes. By cross-checking the contractual specification with the code of a similar rule, I found the root cause (the waiting state existed but no rule led to it) and added a routing rule that queries the reference data with RSQL, applied consistently across the six affected template versions.

Production diagnosis. Faced with an invoice that could no longer be matched, I went back to the raw data instead of the reported symptom: the quantity wasn't zero but negative, and the receipt's metadata proved the bad value came from the import. Platforms run as dozens of Docker containers, monitored with Grafana and internal tools.

Architecture

AutoInit

  • Input: an n8n form (client, DNS alias, environment, modules).
  • Orchestrator: n8n in a Docker container on an internal server, the only machine that can reach both Azure DevOps and the client VMs.
  • Azure DevOps: repository created from the template, configuration branch, merge requests merged by a service account, build pipeline triggered, artifact downloaded through the REST API.
  • Client virtual machine (Azure): artifact uploaded over SFTP, installation and deployment over SSH, all through a bastion that authenticates and logs every session.
  • Verification: counting the Docker containers actually running, then configuring the web front end.
  • Notifications: access details sent to the team, and a dedicated error workflow posts a Teams alert with the failed step and its message.

Streamline for Invoices platform: AngularJS front end, Java back end (REST API, BPMN workflow engine), Node.js rules engine for customizations, PostgreSQL, SFTP file exchanges, all running as Docker containers on a dedicated VM per client. Delivery cycle: development, reviewed merge request, staging for client acceptance, production.

Skills developed

Automation & orchestration (n8n)

Designed an end-to-end n8n workflow with JavaScript code nodes, explicit error handling, an alerting workflow, and configured the n8n server in Docker (environment variables, execution timeouts, allowed modules).

Build & deployment pipeline

Reverse-engineered an undocumented build chain from an existing pipeline, drove Azure DevOps through its API (repositories, merge requests, pipelines, artifacts), deployed over SSH/SFTP through a bastion.

Customizing a standard product

JavaScript and Java business rules, RSQL queries on reference data, SQL scripts and cron jobs, without breaking compatibility with the standard product's updates.

Production diagnosis

Investigating Docker containers, logs (Grafana), PostgreSQL databases and SFTP flows, reasoning from data rather than from the reported symptom.

ISO 27001 security

Least privilege applied to automation: no personal accounts, a service account scoped to the relevant projects, SSH sessions authenticated and logged by the bastion.

Git flow & code review

Feature, release and hotfix branches, every merge request reviewed, commit conventions, and a reviewer role catching obvious issues to save seniors' time.

Code highlights

AutoInit's SSH helper in an n8n code node (simplified)
javascript
1// Exécute une commande sur la VM cible et échoue clairement si le résultat
2// n'est pas celui attendu. L'utilisateur SSH est construit à chaque appel,
3// car il dépend du client et de l'environnement.
4const { NodeSSH } = require("node-ssh");
5
6async function run(target, command, { expectCode = 0 } = {}) {
7  const ssh = new NodeSSH();
8  await ssh.connect({
9    host: target.bastionHost,
10    username: `${target.user}@${target.vm}`,
11    privateKey: target.privateKey,
12  });
13  try {
14    const res = await ssh.execCommand(command);
15    if (res.code !== expectCode) {
16      throw new Error(
17        `[${target.vm}] "${command}" a renvoyé ${res.code}\n${res.stdout}\n${res.stderr}`
18      );
19    }
20    return res.stdout;
21  } finally {
22    ssh.dispose();
23  }
24}
25
26// On ne se fie pas au code retour de l'installation : on vérifie l'état réel.
27const running = await run(target, "docker ps -q | wc -l");
28if (Number(running) < target.expectedContainers) {
29  throw new Error(`Seulement ${running} conteneurs démarrés`);
30}

This small helper put an end to steps that looked successful but weren't. My second version changed n8n's SSH credentials during execution, but n8n caches them when each node starts, so the workflow sometimes connected to the wrong server. Building the user on every call and throwing an explicit error with the full output made every failure visible and understandable.

Results & outcomes

  • About 2 hours of manual work replaced by a one-minute form, followed by 40 to 45 minutes of automated execution that no longer ties anyone up.
  • Adopted immediately: the day after it went live, the team asked me to set up projects with it, and about twenty platforms have been initialized through AutoInit since, by me and by my colleagues.
  • Validated end to end on development and staging environments, from the form to a platform reachable at its public address. A later version removed the last manual action using the bastion's API.
  • Taken over by the company: the tool keeps evolving (automatic VM start, credentials stored in a vault).
  • Customizations in production for many clients, processing real invoices every day. Some clients even reopened a ticket just to say thanks for the work delivered.

Reflection & learnings

AutoInit is what made me choose DevOps. What I enjoyed most this year, automation, pipelines, Docker and working close to production platforms, is exactly that field, hence my DevOps engineering degree, still at ITESOFT.

The lessons I keep:

  1. 01After an asynchronous operation, check the resulting state rather than the call's response. Merging a merge request through the API is asynchronous: chaining too fast, the workflow sometimes built the wrong client's release.
  2. 02Prefer reliability over speed. Parallelizing would have saved ten minutes but made runs unstable.
  3. 03Start from what works when nothing is documented. Analyzing the existing migration pipeline corrected several wrong assumptions in my first versions.
  4. 04Spot a pain point, propose, build it properly and get it adopted. That's what the team expects from an engineer beyond tickets, and it's the approach I want to keep developing.
Next projectTamaStat for TamaBox