Summary
Red Sand Falcon LLC runs a training institute alongside its oil and gas and process-industry businesses in Oman. Every trainee must be registered in the company's Odoo ERP, and registration starts with an ID card or passport. Before this project, trainers photographed ID cards on their own phones and sent them to the Muscat office over WhatsApp. Staff then typed the details into Odoo by hand.
In October 2026, Inzint built a phone app, a server and a document reader that do this job instead. The trainer scans the trainee's Oman Civil ID, Omani driving licence or passport. The app shows the details it read, the trainer confirms them, and the trainee is created in Odoo. Every image stays on the client's own server in Oman. The hard part was not reading a card. It was reading it quickly, with Arabic text, with real phone photos and photocopies, and without ever saving a value the system was not sure about.
The problem: ID cards travelling over WhatsApp
Training often happens far from Muscat, at remote sites. The registration team sits in the Muscat office. So the simplest workflow grew up between them: a trainer photographs each ID card, sends the pictures over WhatsApp, and someone in Muscat types each person's details into Odoo.
Typing by hand was slow and caused mistakes, but that was the smaller problem. The bigger one was where the photos ended up. Copies of identity documents collected on many personal phones, were forwarded between chats, and travelled abroad with their owners. Under Oman's Personal Data Protection Law, misuse of those copies would be a legal problem for the company. The workflow had to stop.
The client described success in one sentence: a smooth, easy app where the trainer registers a trainee just by photographing their ID. Behind that sentence were the real requirements. Near-perfect accuracy on the fields that go into Odoo. Results on screen within seconds. No identity data left on the phone. No image leaving the client's own server.
Why the obvious solution was not enough
The client had already tried the obvious fix: a text-reading screen built inside Odoo. It solved half the problem, because the Muscat team stopped typing everything. But Odoo's limits kept accuracy low, images still had to be uploaded one by one, and trainers were still sending ID photos over WhatsApp to get them to the office. The copies were still everywhere.
Three things made the next attempt harder than it looks. First, the trainee is standing at the desk, so the answer has to come back while they wait, not later from a queue. Second, Arabic. Arabic letters join and change shape, and very few reading models are trained for small, bold card text under a hologram. Third, every image had to stay in Oman, which ruled out the cloud services that make this kind of project easy elsewhere.
What Inzint was responsible for
Inzint owned the whole path from the camera to the Odoo record: the trainer's phone app, the server, the document reader, an admin console for the registration team, the security and data-protection controls, the connection to Odoo, and deployment on the client's own server.
Inzint did not own Odoo. The trainee registration module inside Odoo belongs to the client's Odoo consultant, and our system talks to it through that module's API. Any document other than Oman Civil IDs, Omani driving licences and passports was out of scope, and so were face matching, liveness checks and forgery detection.
How it works: one server in Oman, nothing leaving the country
The trainer's phone takes the photo and sends it over an encrypted connection to a server owned by the client in Oman. The server checks the photo, stores it encrypted, reads it, checks the result against itself, and creates the trainee in Odoo. Anything the system is not sure about goes to the registration team in the admin console instead of into Odoo.
Only the parts that must face the internet do. Everything else stays private on that server. The phone never talks to Odoo directly and never holds an Odoo password.
Where AI is used, and where it deliberately is not
Trained models do the reading: finding the text on the card, reading it in English and Arabic, and finding the portrait. None of them decides anything. Whether a value is correct is decided by fixed checks built into the ID card itself and by comparing different parts of the card with each other. Whether a registration is accepted is decided by rules and by a person. There is no chatbot or generative AI, no cloud AI service, and nothing is trained on customer data.
Engineering decisions and their trade-offs
| Decision | Why | Trade-off and how it was managed |
|---|---|---|
| Reading on the client's own server, no cloud document service | The images cannot leave Oman, and the client did not want a licensed SDK. | Full reading is slower without cloud hardware. Managed by giving the trainer a fast answer first and doing the heavy work after, plus a human review step. |
| A fast check while the trainer waits, deeper reading afterwards | The trainer cannot wait long per card, but a bad photo should be caught before anything is saved. | Two paths to keep in step. The fast check has a strict time limit and never freezes the registration desk. |
| Switching the reading engine mid-project | The first engine failed on real phone photos that looked perfectly clear. | A change of plan during the build. The old engine was kept as a fallback. |
| Building against a stand-in for Odoo | The client's Odoo consultant never gave access to the server, or even to a test copy. | Everything was built and tested against a faithful stand-in, so connecting to the real system was a small, safe step. |
| Node.js and PostgreSQL for the server | Code from the phone app could be reused on the server, and the client's team already backs up PostgreSQL for Odoo. | Two programming languages in the project. The Python part was kept to one small service. |
Challenge 1: a fast answer at the desk
The client wanted near-perfect accuracy and results on screen within seconds. A full, careful read of an ID card on a normal server takes longer than that. Most document-reading systems avoid this by working in the background: you upload a pile and come back later. Here the trainee was standing at the desk. A result that arrives a minute later is useless, and a wrong result that arrives quickly is worse.
We split the work by what the trainer actually needs to see. The app helps the trainer take a good photo in the first place. A quick quality check runs while they wait, with a strict time limit; if anything is slow, the trainer gets a warning, not a frozen screen. The trainer then sees the details, corrects anything wrong and submits. The heavier reading happens after that, so the stored record is checked once more without anyone waiting for it.
When the system cannot verify a field, it does not pretend to. The trainer sees a clear warning, and the record keeps a note of how each value was entered. We did not measure an end-to-end time across real registrations, so this page does not publish one.
Challenge 2: Arabic, and the engine that failed on clear photos
The first version used a well-known open-source reading engine with the classic approach: find the card, straighten it, cut out each field and read it. On clean sample cards it looked fine. Then three clear phone photos of a real Omani card failed on every single field.
A different engine read the same photos without trouble. We moved Civil IDs to the new engine that week and passports soon after, and kept the old engine as a fallback. The Arabic name gets extra care. It is checked against other evidence on the card before it is saved, and if the system is not confident, the registration team decides.
Challenge 3: the files nobody planned for
Every surprise in this project came from real files, never from test data. Real samples stayed in a private folder; only made-up test cards went into automated testing. Each fix below had to pass one rule: uploads that already worked must not change.
Both sides of a card on one page
The client sent real files with the front and back of a resident card on one page, some of them black-and-white photocopies, one an A4 scan with two small, slightly tilted cards. The first version expected one side per page and failed on all of them. The reader now separates the two sides and keeps the result only if both sides check out. If either side is uncertain, the original result stands. Normal uploads never go through this step.
The phone changed the file before the server saw it
One faint photocopy still failed. The cause was not the reader: the phone re-saved the image when it was picked from the gallery, and that damaged the faint machine-readable lines at the bottom of the card. We improved how the reader finds those lines when the text is damaged. On eleven re-saved copies of that file, it now reads 7 instead of 1. The other four go to human review instead of saving a wrong value.
A small tilt that lost a perfect read
A trainee uploaded hand-held photos with the card tilted about five degrees. Every character was read correctly, yet the system said "machine-readable zone not found". Many ID readers expect the card to be level, and ours did too at first. It now straightens the photo before reading and keeps the result only if it is better. In a replay of 74 real cases, it found the machine-readable zone on 40 of 55 uploads, up from 31, with no case made worse.
Challenge 4: Omani ID cards write names the other way round
The international standard for ID cards says the surname comes first in the machine-readable lines. Our first version followed it, and a trainee's English name came out in the wrong order. But, Omani Civil IDs put the given names first. This held on 23 of 23 cards we could check. Omani passports still follow the standard.
The rule now treats Omani Civil IDs differently from everything else. It had to change in the server and in the phone app at the same time, or the two would disagree.
The debugging story: the blue face
The client uploaded a PDF of an ID card and the portrait came back with blue skin. Photos were fine. The cause was a library that turns PDF pages into images: it returned colours in a different order than our code expected, so red and blue were swapped on every PDF page. Every test had passed, because every test PDF went through the same mistake. The fix was one line.
That fix then broke something else: the check that decides which way up a card is. It had been tuned on the wrong colours and only worked because of the bug. We rebuilt the check so it no longer depends on that, and added a colour test so the swap cannot come back silently.
What almost went wrong
The Odoo access that never came
We expected normal access to the client's Odoo and server. Early on it became clear the client's Odoo consultant would not give access to the live system, or even a test copy. What arrived was a module that imitated the real one. The project did not stall, because from day one every Odoo call went through a stand-in that behaves like the real thing, including duplicates, timeouts and errors.
The "easy" camera change that was rolled back
The client asked for the live scanner to work in both landscape directions, "if easy". It took three test builds in one day, and each had a different problem. All of it was undone. The rule that came out of it: camera changes cannot be proven by automated tests, so they never ship as the client's only build.
What we deliberately did not do
- No cloud document service from Azure, Google or AWS, and no commercial reading SDK. Images stay on the client's server in Oman.
- No chatbot or generative AI anywhere in the process. The job did not need one, and a cloud AI service would have moved the data.
- No face matching, liveness or forgery detection. The app only finds and crops the portrait.
- No training on customer data. Nothing is learned from trainee records.
- No documents other than Oman Civil IDs, Omani driving licences and passports.
Security, testing and delivery
Every stored image is encrypted, and files are shown only to signed-in staff. Images are deleted automatically after a set period. On the phone, photos exist only while they are being uploaded and are deleted straight after. Screens showing ID data block screenshots on Android, and the app contains no analytics or crash-reporting tools that could receive personal data. Each trainer is tied to one device, and notification emails carry no trainee personal data.
Real ID samples stayed private and were used only to replay real cases. Automated tests run on made-up cards, and a nightly accuracy check fails the build if reading gets worse. The iPhone app is on the App Store as an unlisted app under the client's own developer account. The Android app is a signed build shared through limited-use links. Trainers join only by invitation.
These controls were built to meet the client's Personal Data Protection Law requirements. This page does not describe the system as certified or audited.
What was delivered
- A phone app for iPhone and Android that guides the trainer through scanning a Civil ID or passport, shows the details for review and submits the registration, in English and Arabic.
- A server that checks each photo, stores it encrypted, reads it, checks the result and flags anything uncertain.
- A document reader on the client's own server that handles phone photos, gallery images, PDFs, two-sided photocopies and A4 scans.
- An admin console where the registration team reviews, corrects and approves registrations and manages trainers and devices.
- A connection to the client's Odoo trainee registration, with automatic retries.
- Accepted by the client in October 2026.
Measured results
- Machine-readable zone found on 40 of 55 real uploads in a replay of 74 cases, up from 31, with no case made worse.
- A faint photocopy re-saved eleven ways: 7 of 11 read correctly, up from 1. The rest go to review instead of saving a wrong value.
- Omani name order: confirmed on 23 of 23 staging cards we could check.
What changed for the client
- ID documents no longer travel over WhatsApp or sit in personal phone galleries. They exist, encrypted, on one server in Oman and nowhere else.
- The registration team reviews exceptions instead of typing every card.
- Nothing the system is unsure about is written to Odoo as fact. It goes to a person.
We did not measure end-to-end speed, volume or time saved, so no such figure is published here.
What we learned
- Real files beat test files. Every important problem in this project showed up on real photos, scans or staging data first.
- Add a careful extra path; do not loosen the one that works.
- Let the document check itself. The card's own check digits decide whether a reading is kept, so reading more never means guessing.
- Fixing a bug can break code that quietly depended on it.
- Camera code needs a real phone. Automated tests cannot prove it.
- Plan for the system you cannot see. A good stand-in meant weeks without Odoo access cost the schedule nothing.
What this shows
This project shows that Inzint can read Omani ID cards and passports from real phone photos, scans and photocopies into a live business system, keep every image on the client's own server in Oman, and never save a value the system could not verify. It also shows that problems found in real use can be fixed without breaking what already works.
The same needs apply to any Omani organisation that registers people with an ID at a counter: training institutes, clinics, hotels, car rental, telecom shops and recruitment agencies. The data has to stay in Oman, the answer has to come while the person waits, and the system has to be honest about what it is not sure of.

