The KAGAMI mark КАГАМИ
kagami.bg/academy · lesson · machine-readable viewVERIFIED 2026-10-01 · UPDATED 2026-10-01
IDENTITY
module
GX10-04-127 · Invoice OCR with Nemotron OCR v2, EIK checksum and VIES
series
GX10 (local AI server class: NVIDIA GB10, e.g. ASUS Ascent GX10 / DGX Spark)
level
Intermediate
duration
2–3 h
prerequisites
A GB10-class machine with Docker and the NVIDIA Container Toolkit, a free NVIDIA NGC account with an API key, Python 3 with the requests package, a few invoices of your own (or the fictional example in this lesson)
trust_label
VERIFIED 2026-10-01 (model card, NIM documentation, VIES endpoints and Bulgarian VAT Act text read at the sources) · UPDATED 2026-10-01 · NOT TESTED (no TESTED label: the main step, OCR on GB10, was not run); sandbox runs only: the checking code (EIK checksum, amounts, field parsing, a live VIES call) was run in a sandbox; the NIM container on GB10 and OCR quality on real Bulgarian invoices were NOT tested
versions
Nemotron OCR v2 (model id nvidia/nemotron-ocr-v2, build.nvidia.com release 2026-04-15) · NIM container image tag 2.0 (release notes list 2.0.1) · VIES REST and SOAP as published 2026-10-01
language
english edition · bulgarian human view: /academy/gx10/ (same file name)
previous / next
04-116_Morning_Dashboard_Appsmith.html / 04-128_Expense_Auto_Classification.html · related: 04-132, 04-134, OCR engines 04-213, 04-214, 04-215 · see also 04-131 (same topic, other approach)
PURPOSE

Read invoices on a local GB10-class server with Nemotron OCR v2 served as a NIM, turn the OCR text into fields, and validate them: Bulgarian EIK by checksum, amounts (net + 20% VAT = total, in euro), required-field completeness against article 114 of the Bulgarian VAT Act, and the VAT number in VIES. Anything doubtful goes to a human; nothing is booked on OCR output alone.

KEY CONCEPTS
COMMANDS / PATHS
CHECKLIST
NEXT MODULE

04-128 · Expense classification to the chart of accounts (04-128_Expense_Auto_Classification.html) · related: 04-132 bank reconciliation, 04-134 annual accounts, 04-213/04-214/04-215 OCR engines · see also 04-131 (invoice OCR with another stack) · offer: Quick experiment (kagami.bg/stalbata/)

SOURCES
TAGS
gx10nvidia-gb10ocrnemotron-ocrniminvoiceseikviesvatbulgaria
VERIFIED · 01.10.2026 UPDATED · 01.10.2026

Invoice OCR on GX10: Nemotron, EIK and VIES

We read invoices with OCR on a local AI server of the NVIDIA GB10 class and check them like a careful accountant: are the required fields there, does the EIK pass its checksum, do the amounts add up with 20% VAT, and does the VAT number exist in VIES. Anything doubtful goes to a person — nothing is booked on OCR output alone.

⏱ 2–3 h Intermediate GX10 Nemotron OCR v2 · NIM · Python EIK · VAT Act · VIES
Nemotron OCR v2 (NIM in a container)🔒 local EIK and amount checks (Python)🔒 local VIES of the European Commission🌐 global
🔄
UPDATED · 01.10.2026 — what changed
We fixed the foundation of the lesson. The old version ran nemotron-ocr:1.0.0 and sent a field language: ["bg","en"] — there is no such field in the documentation, and the first model version (v1) is English-only. Now: Nemotron OCR v2, image nemotron-ocr-v2:2.0, the current request format (/v1/ocr with an input array) and a response with text_detections. We fixed the EIK check: the 13-digit form was always returned as “valid” — now it is checked. Amounts are in euro with exact decimals (Decimal), not in leva with floating-point numbers. The VAT number is checked in VIES with the real address and the real response. We added: a check of the required invoice particulars under article 114 of the VAT Act (quoting the law), the VIES terms, and proper handling of “VIES does not answer”. We removed: unconfirmed figures (the error rate of manual entry), n8n flows and schedules that were not verified, and the overlap with 04-131.
⚠️
What we have not run ourselves
Tested in a sandbox (not on a GB10): the EIK check (against VIES and against an independent library), the amount check, field extraction from a made-up text and one live request to VIES. Not tested: the NIM container on a GB10, its speed, and — most important — how accurately it reads Bulgarian invoices. Bulgarian is not in the model's list of languages (see step 2). That is why there is no “TESTED” label. Measure on your own invoices before any automation.

01What you will learn

02Before you start

💡
A made-up invoice
All companies and numbers in the lesson are made up: “Primer” Ltd with EIK 100000019 and “Obrazets” Ltd with EIK 100000040. We generated them with the checksum algorithm so they pass the check — that does not mean they cannot coincide with a real number by chance, so do not use them anywhere but here.

03Steps

  1. The task

    An example scenario (not a measurement): an accounting firm receives 200 invoices a month and types them in by hand, about 4 minutes each — 800 minutes, i.e. about 13 hours. The idea is for the computer to read the invoice and for a person to look only at those where something does not add up. That is why the lesson is about the checks as much as about the reading itself: OCR makes mistakes, and a wrong amount in the books costs more than lost time.

  2. Which model, and what is verified about it

    NVIDIA has a Nemotron OCR family. The facts from the model cards (Hugging Face and build.nvidia.com) and the NIM documentation as of 01.10.2026:

    WhatValue
    Namenvidia/nemotron-ocr-v2 (the older nemotron-ocr-v1 is English-only per its card)
    What it doesFinds text regions, recognises them and works out the reading order (detector + recogniser + relational model); returns text, boxes and a confidence from 0 to 1
    Variants in v2v2_english and v2_multilingual (the default in the NIM)
    Languages (multilingual)English, Chinese (Simplified and Traditional), Japanese, Korean, Russian (the card text). The card's tags also list Spanish, French, German, Italian, Dutch and Portuguese. Bulgarian is not listed anywhere.
    Licencebuild.nvidia.com and the Hugging Face card: NVIDIA Open Model License Agreement (post-processing scripts — Apache 2.0). The NIM documentation names other texts for the container (see the note below)
    Hardware for the NIMThe NIM v2 support table includes NVIDIA GB10 (such as DGX Spark), FP16
    Released in v215.04.2026 (build.nvidia.com)
    ⚠️
    Bulgarian: unverified
    Cyrillic is not excluded — Russian is on the list and the model's character set is large — but NVIDIA promises nothing for Bulgarian. We claim neither “it works” nor “it does not”: that is why the “Check” section includes a step to measure on your own invoices. Be especially careful with digits, EIK and amounts — one wrong character spoils everything.
    ✅
    The licence: read it before commercial use
    The sources do not agree. The model card says “NVIDIA Open Model License Agreement” and “ready for commercial use”. The NIM documentation's terms page names, for the container, the NVIDIA Software License Agreement and Product Specific Terms for AI Product, and for the model the AI Foundation Models Community License Agreement. We do not interpret this for you: open the current texts (links in “Sources”) and give them to a lawyer if you will sell a service.
  3. Start the OCR service (NIM)

    A NIM is a ready-made container: it downloads the model and opens a small web interface (an API). Follow the official documentation; here is the essence. The key goes into an environment variable, not into the command:

    bash · on the machine
    export NGC_API_KEY=<your-NGC-key>
    echo "$NGC_API_KEY" | docker login nvcr.io --username '$oauthtoken' --password-stdin

    The word $oauthtoken is literally the user name (that is how NVIDIA's documentation has it — it means “I log in with a key”). Then:

    bash · on the machine
    export LOCAL_NIM_CACHE=~/.cache/nim
    mkdir -p "$LOCAL_NIM_CACHE/cache" "$LOCAL_NIM_CACHE/weights"
    
    docker run -it --rm --name nemotron-ocr-v2 \
      --runtime=nvidia \
      --gpus '"device=0"' \
      --shm-size=16GB \
      -e NGC_API_KEY \
      -e NIM_ENGINE_MODEL_DOWNLOAD_PROVIDER=ngc \
      -v "$LOCAL_NIM_CACHE/cache:/opt/cache" \
      -v "$LOCAL_NIM_CACHE/weights:/model" \
      -u $(id -u) \
      -p 8000:8000 \
      nvcr.io/nim/nvidia/nemotron-ocr-v2:2.0

    In another terminal, check that it is ready (the first download takes a while):

    bash
    curl http://localhost:8000/v1/health/ready

    You expect {"object":"health.response","message":"ready","ready":true}. The variant is chosen with -e NIM_ENGINE_MODEL_VARIANT=english (the default is multilingual). Port 8000 is from the documentation; keep it reachable only on the machine itself if the service will not be called from outside.

    💡
    Why the container, not pip
    The model card on Hugging Face describes an installation for Linux amd64 and data-centre GPUs. On a GB10 (Arm) the supported path is the NIM container — it is explicitly in the support table. ⚠️ What exactly nvidia-smi will show and how it runs on your machine we have not measured.
  4. Send an invoice and get text

    The request format is from the NIM v2 documentation: an input array with images as a base64 data: address and a merge level for the text (paragraph is the default). For each piece of text the response gives a confidence — we will use it.

    python · invoice_check.py (1/3)
    import base64, re
    from decimal import Decimal, ROUND_HALF_UP
    from datetime import datetime
    
    NIM = "http://localhost:8000"
    
    def ocr_page(path: str):
        """Returns (text, mean confidence) for one JPEG or PNG page."""
        import requests
        mime = "image/png" if path.lower().endswith(".png") else "image/jpeg"
        with open(path, "rb") as f:
            b64 = base64.b64encode(f.read()).decode()
        body = {"input": [{"type": "image_url",
                           "url": f"data:{mime};base64,{b64}"}],
                "merge_levels": ["paragraph"]}
        r = requests.post(f"{NIM}/v1/ocr", json=body, timeout=60)
        r.raise_for_status()
        det = r.json()["data"][0]["text_detections"]
        lines = [d["text_prediction"]["text"] for d in det]
        confs = [d["text_prediction"]["confidence"] for d in det]
        return "\n".join(lines), (sum(confs) / len(confs) if confs else 0.0)

    Notes: a PDF is first turned into images (one per page); for faster reading the documentation advises JPEG. ⚠️ The order of the pieces is whatever the service returns — check on your own invoices that the reading order is sensible.

  5. What an invoice must contain (article 114, VAT Act)

    Before extracting, we know what we are looking for. The quotation is from the consolidated text of the Bulgarian Value Added Tax Act (article 114(1) — in force from 01.01.2026), as published on lex.bg (our translation of the Bulgarian text):

    No.The invoice must containCan OCR “catch” it?
    1name of the documentyes (“Invoice”)
    2a sequential ten-digit number containing only Arabic digits, … which identifies the invoice uniquelyyes — 10 digits
    3date of issueyes
    4name and address of the supplierpartly (free text)
    5the supplier's identification number under art. 94(2) … or the number under art. 84 of the Tax and Social Security Procedure Code, where the supplier is not VAT-registeredyes — VAT number or EIK
    7, 8name and address of the recipient; the recipient's identification number (rules vary by case)partly
    9quantity and kind of the goods, kind of the servicefree text
    10the date of the taxable event, or of the payment receivedoften not shown separately
    11the unit price excluding tax and the taxable base of the supply, and any discounts granted …yes — taxable base
    12the tax rate (for a zero rate — the basis; the basis for not charging tax)yes
    13the amount of the taxyes
    14the amount payable, if it differs from the taxable base plus the taxyes

    We left out item 6 (repealed) and item 15 (new vehicles). Two important points from the same article: under paragraph 7 the invoice may omit items 12, 14 and 15 when the person is in a special small-business scheme, or when the sum of the taxable base and the tax does not exceed 100 euro (except supplies located in another member state, intra-community supplies and distance sales). Under paragraph 5 amounts may be in any currency provided the taxable base and the tax are shown in euro. The law is long and changes — this is not legal advice.

    💡
    Why this matters for the program
    A missing “required” particular does not necessarily mean OCR erred — the invoice may genuinely be incomplete. In both cases a person must see it. That is why the program below does not “fix” anything but returns reasons for review.
  6. From text to fields

    This is the most fragile part: invoices differ. The regular expressions below are for the made-up invoice in the lesson — your invoices will need their own. Start simple and add rules only when you see an error on a real invoice.

    python · invoice_check.py (2/3)
    def money(txt: str) -> Decimal:
        """'1 234,56' -> Decimal('1234.56')."""
        return Decimal(txt.replace(" ", "").replace(" ", "").replace(",", "."))
    
    def iso_date(txt: str):
        for fmt in ("%d.%m.%Y", "%Y-%m-%d"):
            try:
                return datetime.strptime(txt, fmt).date().isoformat()
            except ValueError:
                pass
        return None
    
    PATTERNS = {
        "number": r"(?:Фактура|ФАКТУРА)\s*(?:№|No\.?)?\s*(\d{10})\b",
        "date":   r"Дата(?: на издаване)?\s*:?\s*(\d{2}\.\d{2}\.\d{4})",
        "supplier_eik": r"Доставчик[\s\S]{0,200}?ЕИК\s*:?\s*(\d{9}(?:\d{4})?)",
        "buyer_eik":    r"Получател[\s\S]{0,200}?ЕИК\s*:?\s*(\d{9}(?:\d{4})?)",
        "supplier_vat": r"Доставчик[\s\S]{0,300}?ИН по ЗДДС\s*:?\s*(BG\d{9,10})",
        "net":   r"Данъчна основа\s*:?\s*([\d\s,]+)\s*EUR",
        "vat":   r"ДДС\s*20\s*%\s*:?\s*([\d\s,]+)\s*EUR",
        "total": r"(?:Сума за плащане|Общо)\s*:?\s*([\d\s,]+)\s*EUR",
    }
    
    def parse(text: str) -> dict:
        out = {}
        for k, p in PATTERNS.items():
            m = re.search(p, text)
            out[k] = m.group(1).strip() if m else None
        return out

    The patterns match Bulgarian words on purpose — they read a Bulgarian invoice (“Доставчик” = supplier, “Получател” = recipient, “Данъчна основа” = taxable base, “ИН по ЗДДС” = VAT identification number). The number of digits in the VAT number (9 or 10) is our choice: the law (art. 94(2)) says only that the number begins with “BG”, not how many digits it has. ⚠️ A ten-digit number is often the personal ID of an individual — see “Personal data” below.

  7. EIK: the checksum

    An EIK has a last digit calculated from the preceding ones. If OCR misreads a digit, the checksum will almost surely fail — so we catch a wrong reading without the internet. The algorithm for 9 digits: digits 1 to 8 are multiplied by 1, 2, … 8 and summed; the remainder on division by 11 is the check digit. If the remainder is 10, repeat with weights 3, 4, … 10; if it is 10 again, the check digit is 0.

    13 digits. The official text — Ordinance No 1 of 14.02.2007 on the commercial register, art. 114(3) and (4) (published on the Registry Agency website) — says only that the EIK has 9 digits and that the EIK of a branch is a 4-digit code appended to the 9-digit one. It gives no check-digit rule for the last 4 digits. So for 13 digits the program checks only the first 9 and sends the invoice to a person. The rule with weights 2, 7, 3, 5 (on remainder 10 — 4, 9, 5, 7) found in various programs is shown for reference only — it is not confirmed by an official source and plays no part in the decision.

    python · invoice_check.py (3/3)
    def eik_check_digit9(first8: str) -> int:
        s = sum(int(d) * w for d, w in zip(first8, range(1, 9))) % 11
        if s == 10:
            s = sum(int(d) * w for d, w in zip(first8, range(3, 11))) % 11
        return 0 if s == 10 else s
    
    # For reference only: UNOFFICIAL rule for a 13-digit EIK (branch).
    # Not in Ordinance No 1/2007 and not used in validate_eik().
    def eik_check_digit13(first12: str) -> int:
        last4 = first12[8:12]
        s = sum(int(d) * w for d, w in zip(last4, (2, 7, 3, 5))) % 11
        if s == 10:
            s = sum(int(d) * w for d, w in zip(last4, (4, 9, 5, 7))) % 11
        return 0 if s == 10 else s
    
    def validate_eik(eik: str) -> bool:
        """9 digits: full check. 13 digits (branch): only the first 9
        are checked — there is no official rule for the last 4."""
        if not (eik.isascii() and eik.isdigit()) or len(eik) not in (9, 13):
            return False
        return int(eik[8]) == eik_check_digit9(eik[:8])
    ✅
    Tested in a sandbox · 01.10.2026
    We ran the code against: three public EIKs that VIES confirmed as valid (all passed), three numbers VIES rejected (two of them a valid one with the last digit changed; all failed), and 20,000 random 9-digit numbers — the result matched the Python library python-stdnum in all 20,000 (including the second-weights case). The made-up EIKs 100000019 and 100000040 pass; 100000086 goes through the second set of weights. The 13-digit form (review 01.10.2026): the official Ordinance No 1/2007 (art. 114(3)–(4)) has no check digit for the branch code, and python-stdnum (version 2.2) checks only 9- and 10-digit numbers. So the code above checks only the first 9 digits of a 13-digit EIK, and review_reasons sends the invoice to a person.
    ⚠️
    What the checksum does NOT prove
    An EIK that passes its checksum is only well-formed — it does not mean the company exists or is VAT-registered. And the other way round: “invalid” in VIES does not mean “fake”, most often it means “not VAT-registered”. For example, our made-up numbers are “invalid” in VIES yet pass the checksum.
  8. Amounts: base, 20% VAT and total in euro

    The standard rate is 20% (art. 66(1) of the VAT Act). The check is simple but works only for invoices with one rate — with mixed rates or a zero rate we skip it and send the invoice to a person. We use Decimal, not float: with ordinary floating-point numbers money is rounded inaccurately.

    python · addition
    def check_amounts(net: Decimal, vat: Decimal, total: Decimal):
        cent = Decimal("0.01")
        exp_vat = (net * Decimal("0.20")).quantize(cent, ROUND_HALF_UP)
        if abs(vat - exp_vat) > cent:
            return False, f"ДДС {vat} != 20% от {net} = {exp_vat}"
        if abs(total - (net + vat)) > cent:
            return False, f"Общо {total} != {net} + {vat}"
        return True, "OK"

    The tolerance of one euro cent is our choice for line rounding. Invoices in another currency (art. 114(5)) need the base and tax in euro — the program sends them to a person until you add a rule. The same goes for reverse charge: under art. 114(4), when the tax is payable by the recipient, the invoice shows neither the tax amount nor the rate, and states "reverse charge" with the legal basis — the 20% check will send such an invoice for review, which is correct. (The messages in the code are in Bulgarian because the invoices are.)

  9. The VAT number in VIES

    VIES is the European Commission's service for confirming VAT numbers. There are two ways; we checked both on 01.10.2026:

    WayAddressWhat it returns
    REST (simpler)GET https://ec.europa.eu/taxation_customs/vies/rest-api/ms/{country}/vat/{number}JSON: isValid, userError (e.g. VALID/INVALID), name, address
    Which countries answerGET …/vies/rest-api/check-statusfor each country “Available” or “Unavailable”
    SOAPdescription: …/vies/checkVatService.wsdloperations checkVat and checkVatApprox (with name and address matching and a confirmation number)

    For Bulgaria the country is BG and the number is given without the prefix. Example: …/ms/BG/vat/100000019.

    python · addition
    def vies_check(country: str, number: str):
        """True / False, or None if VIES cannot answer."""
        import requests
        url = f"https://ec.europa.eu/taxation_customs/vies/rest-api/ms/{country}/vat/{number}"
        try:
            r = requests.get(url, timeout=10)
            r.raise_for_status()
            return r.json().get("isValid")
        except Exception:
            return None   # we do not know -> a person, not "invalid"

    The terms of the service are in the description of the official SOAP file, and this is what they say (in substance): the purpose is for persons making intra-community supplies to confirm the validity of a VAT number; any other use, extraction of data and retransmission are forbidden; the Commission accepts no responsibility for the data, which comes from the member states' databases; a “valid” answer does not in itself give a right to exempt a supply from VAT; under load the service returns errors such as MS_UNAVAILABLE, MS_MAX_CONCURRENT_REQ, GLOBAL_MAX_CONCURRENT_REQ or TIMEOUT — try again later. So: ask only when you need to, remember the answer, do not send bulk requests.

    🌐
    The data leaves your machine
    Unlike OCR, here the VAT number goes to a European Commission server, and the response carries the company's name and address. That is normal for companies, but for individuals (a personal ID as the number) it is personal data — do not query it and do not keep it without a basis. ⚠️ We have not asked a lawyer; do so if you will run the service for clients. The REST interface was checked with a live request; VIES's technical-terms page is dynamic and we did not read it, so the terms above are from the official WSDL.
  10. Decision: pass, review, reject

    We tie everything together: for each invoice we collect reasons for review. An empty list does not mean “book it” — it means “may be ready for a person to approve”.

    python · addition
    def review_reasons(f: dict, mean_conf: float, min_conf: float = 0.90):
        why = []
        for k in ("number", "date", "supplier_eik", "net", "vat", "total"):
            if not f.get(k):
                why.append(f"липсва: {k}")
        if f.get("supplier_eik") and not validate_eik(f["supplier_eik"]):
            why.append("ЕИК с грешна контролна сума")
        if f.get("supplier_eik") and len(f["supplier_eik"]) == 13:
            why.append("ЕИК на клон: последните 4 цифри не са проверени")
        if f.get("date") and not iso_date(f["date"]):
            why.append("невалидна дата")
        if f.get("net") and f.get("vat") and f.get("total"):
            ok, msg = check_amounts(money(f["net"]), money(f["vat"]), money(f["total"]))
            if not ok:
                why.append(msg)
        if mean_conf < min_conf:
            why.append(f"ниска увереност {mean_conf:.2f}")
        return why

    The threshold 0.90 is a starting value for learning, not a proven limit: tune it to the errors you see on your own invoices. VIES is called separately (it needs the internet) and only for a supplier with a VAT number.

    Try it with the made-up invoice — the text OCR should return:

    python · trial
    text = """ФАКТУРА № 0000000042
    Дата на издаване: 15.09.2026
    Доставчик: Пример ООД (фиктивна фирма)
    ЕИК 100000019
    ИН по ЗДДС: BG100000019
    гр. Варна, ул. Примерна 1
    Получател: Образец ЕООД (фиктивна фирма)
    ЕИК 100000040
    Описание: Консултантски услуги, 10 часа
    Данъчна основа: 1 000,00 EUR
    ДДС 20 %: 200,00 EUR
    Сума за плащане: 1 200,00 EUR
    """
    f = parse(text)
    print(f)
    print(review_reasons(f, 0.97))                            # []
    print(review_reasons(parse(text.replace("200,00", "210,00", 1)), 0.97))
    print(review_reasons(parse(text.replace("ЕИК 100000019", "ЕИК 100000018")), 0.97))

    You expect: the fields recognised; an empty list; ['ДДС 210.00 != 20% от 1000.00 = 200.00']; ['ЕИК с грешна контролна сума']. That is exactly what we got in the sandbox test.

  11. Storage and next steps (briefly)

    An invoice fit for review is stored in a database — amounts as NUMERIC, currency defaulting to euro, plus the review reason and the whole OCR response for later audit. A minimal example (⚠️ not run in Postgres):

    sql · PostgreSQL
    CREATE TABLE invoices (
      id             BIGSERIAL PRIMARY KEY,
      received_at    TIMESTAMPTZ DEFAULT NOW(),
      invoice_number VARCHAR(10),
      invoice_date   DATE,
      supplier_eik   VARCHAR(13),
      buyer_eik      VARCHAR(13),
      amount_net     NUMERIC(12,2),
      vat_amount     NUMERIC(12,2),
      amount_total   NUMERIC(12,2),
      currency       CHAR(3) DEFAULT 'EUR',
      ocr_confidence NUMERIC(4,3),
      status         VARCHAR(16) DEFAULT 'pending',  -- pending | approved | rejected
      review_reason  TEXT,
      raw_ocr_json   JSONB,
      UNIQUE (supplier_eik, invoice_number, invoice_date)
    );

    The automation stack (n8n, Postgres) is in the lesson n8n on GX10. Expense types and categories are a separate topic — we do not touch them here; see the lesson Chart of Accounts Expenses. If you are unsure how to record an accounting event, ask your accountant: the lesson does not replace accounting or legal advice.

  12. Personal data and security — the minimum

    • Invoices contain names, addresses, sometimes personal ID numbers and bank accounts. The OCR runs locally — that is the advantage over a cloud OCR.
    • The NGC key lives in an environment variable, not in a file in Git.
    • Keep copies of invoices and OCR results in a protected place; share only what is needed.
    • Only companies' VAT numbers go to VIES, and only when needed.
    • A person approves every booking.

04Check

Quiz

1. What does the model card say about the languages of Nemotron OCR v1?

2. What does a valid EIK checksum prove?

3. VIES returns “invalid” for a VAT number. What is the most likely meaning?

4. According to article 114(1) of the VAT Act, what form does an invoice number have?

05What next

Related lessons: Chart of Accounts Expenses · Bank Reconciliation: Movements to Invoices · Annual Accounts from Postgres to Excel · for reading the text itself — Tesseract, EasyOCR, PaddleOCR. See also: lesson 04-131 in the series, which covers the same topic — invoice OCR — with another approach (TrOCR and a language model).

06Sources

  1. Nemotron OCR v2: model card · v1 🔒 local — languages, licence, hardware.
  2. build.nvidia.com: nemotron-ocr-v1 — terms of use.
  3. NIM for OCR: support matrix · get started · API · release notes · terms — GB10, image, request format.
  4. VIES of the European Commission · REST: availability · SOAP description with the terms 🌐 global.
  5. Value Added Tax Act (lex.bg, in Bulgarian) — articles 66, 94, 114 (consolidated text, read on 01.10.2026).
  6. python-stdnum — the independent library we used to cross-check the EIK check.
  7. Ordinance No 1 of 14.02.2007 (Registry Agency, in Bulgarian) — art. 114(3)–(4): the EIK has 9 digits, a branch EIK appends a 4-digit code (read on 01.10.2026).