IRONSOFTWAREHOME

How to Use IronQR with ChatGPT Codex

Curtis Chau
Curtis Chau
Updated: October 9, 2026

QR code backlogs often arrive as disorganized folders containing scanned asset tags, on-site phone photos, fax copies, zero-byte files from scanner errors, and cover sheets without codes. A naive loop that processes one file at a time halts at the first corrupted file and does not track which images failed or the reasons for failure.

ChatGPT Codex is OpenAI's coding agent. It operates in terminals, IDEs, the ChatGPT desktop app, and the cloud, all under a single ChatGPT sign-in. IronQR is Iron Software's .NET QR code library.

Each prompt instructs Codex to perform the task and generate a results file holding one row per image. Since batch jobs may process files that yield no results, all counts referenced in this guide are derived from these results files, as indicated in the prompts.

Quickstart
NuGetInstall with NuGet

PM > Install-Package IronQR

Install IronQR by running the command above in the NuGet Package Manager Console, or search for the package in the NuGet Package Manager.

Start Codex within a .NET console project containing your image folder, and specify the batch processing requirements.

$ironqr Add the IronQR package to this console project, then make Program.cs
decode every file in Backlog/sample/ with QrReader.ReadAsync. Write
out/quickstart.csv with one row per file (name, decoded value or empty, QR
type, exception type if the file threw) and print the decoded, empty, and
threw counts by reading that CSV back. Load the key out of the
IRONQR_LICENSE_KEY variable. Call Dispose() on each QrImageInput yourself
instead of a using block, which throws in IronQR 2026.10.1. Run dotnet build
and dotnet run and fix whatever fails.
Text

The $ironqr mention loads IronQR's skill before Codex writes code.



What is ChatGPT Codex, and how does it fit into .NET QR code work?

Codex works inside the permissions you grant it. It writes the decoder, runs it over the images, reads the output, and fixes the code. Install the CLI with npm install -g @openai/codex from the Codex CLI repository, and see the Codex CLI documentation for terminal use.

A backlog with ground truth makes review short. An invented member fails the build, and a wrong decode shows up as a row that disagrees with Backlog/manifest.csv.

The agent may structure each batch differently, but the decoder has to return the same value for a given image at any thread count. That consistency belongs to the library, and IronQR covers the parts a backlog needs.

  • Photos and scans in one call: the QrScanMode.Auto mode runs the detection model and, when the model finds nothing, retries with a basic scan. The scan modes guide compares it with the two single-pass modes.
  • The symbol type on every result: QrResult.QrType separates standard QR (QrEncoding.QRCode) from Micro QR and rMQR, so a validator can reject labels that are not asset tags.

How do you write an IronQR prompt and set up Codex for it?

An effective IronQR batch prompt states six things. For this backlog, the input is every PNG and JPEG in Backlog/images/, a SemaphoreSlim sized by a --concurrency option sets the concurrency, and the output is out/pipeline.csv with one row per file. A file that throws gets a row while the batch continues. The proof is a row-for-row match with out/sequential.csv, and the constraints are no new packages and Auto as the default.

OpenAI's research on hallucination finds that training and evaluation reward guessing, and in a batch job the guess is usually a total in the closing summary.

Tips: Before you send a batch prompt, find the output file it names.

Codex discovers skills in the repository's .agents/skills folder, and each skill is a folder holding a SKILL.md with a name and a description in its frontmatter. Save IronQR's skill.md as .agents/skills/ironqr/SKILL.md and the $ironqr mention resolves. Codex can also pick the skill without the mention when a task matches its description.

AGENTS.md carries the rules every session starts with. Codex reads at most one such file from each directory between the repository root and the folder it was started in, and a deeper file overrides the guidance above it. The /init command drafts one. The prompt below also names IronQR's llms.txt index as an API reference.

Create the repository's AGENTS.md with a section titled QR decoding. It
should say that IronQR (package IronQR, namespace IronQr) is the only QR
library here, that the ironqr skill in .agents/skills/ironqr/ and IronQR's
llms.txt documentation index are the API references, that the key is read
from IRONQR_LICENSE_KEY and never printed or written to a file, that
QrImageInput is disposed with an explicit Dispose() call and never a using
block, that Backlog/manifest.csv is ground truth and read-only, and that any
count, rate, or timing you report must come from a per-image results file
written in the same turn, named in your reply.
Text

How do you ask Codex to add IronQR and decode a first folder?

Show me the IronQR PackageReference in AssetBacklog.csproj and the installed
version. Then run dotnet list package --include-transitive and quote the lines
for IronQR.Slim and Microsoft.ML.OnnxRuntime, since the detection model
depends on them. Change no package references.
Text

The IronQR package brings in IronQR.Slim for the managed API, Microsoft.ML.OnnxRuntime for detection, and the Windows native readers. A project on IronQR.Slim alone, which on Windows also needs IronSoftware.ReaderInternals.Windows, still reads with the basic scan but cannot run the detection model. The package is IronQR with a capital R while the namespace is IronQr, so a using IronQR directive fails to compile. Check the IronQR changelog before you pin a release.

IronQR adds no trial watermark to reads. An unlicensed first read throws LicensingException with the message "Production License Required", and with ReadAsync the exception surfaces when the task is awaited. In a batch, that becomes one identical failure row per image.

Move the license setup into a Startup.ApplyIronQrKey method called first in
Program.cs. If IRONQR_LICENSE_KEY is unset or License.IsLicensed is false,
exit with code 2 and one clear line on stderr before any image is opened,
because IronQR throws on the first read instead of degrading. Run the program
once as is and once with the variable removed for that single command, and
report both exit codes and what IsLicensed returned.
Text

The license keys page covers trial keys and the configuration files IronQR also reads. The exception type is internal to the IronQr assembly, so catching LicensingException by type fails to compile with error CS0122.

A sequential decoder comes next, and its results file becomes the reference for every faster mode.

Create Services/BacklogDecoder.cs with DecodeAsync(string path) returning an
ImageOutcome record: file, status (Decoded, Empty, Unreadable), values, QR
types, exception type and message, and milliseconds. Open the file with
AnyBitmap.FromFile, read with QrScanMode.Auto, and never let one file stop
the batch. Add a --sequential mode that runs it over Backlog/images/ one file
at a time, writes out/sequential.csv, and starts out/runs.csv with the mode
and wall time. Report the status counts and the wall time from those files.
Text

How do you decode a folder in parallel with IronQR?

The async and multithreading guide calls a separate reader per iteration the safe default, because IronQR makes no explicit thread-safety guarantee for a shared instance. The next prompt has Codex's built-in explorer agent, a read-heavy agent for codebase exploration, check that against the documentation before anything goes parallel.

Ask the explorer agent to answer this without editing anything. Before the
decoder goes parallel, list every QrReader, QrImageInput, and AnyBitmap that
BacklogDecoder creates or keeps, say which could be shared across threads,
and back each answer with a quote from
https://ironsoftware.com/qr/csharp/how-to/async-multithreading/ or the
ironqr skill. Flag anything the documentation leaves unstated.
Text

In calibration, sharing one reader across 32 threads produced the same results as a reader per file. A later release may differ, and a reader per file costs little.

The input object needs care too. In version 2026.10.1, a using block around it throws NotImplementedException on exit, even after a successful read, so a batch would record every good image as a failure. Review each diff for one.

For the parallel version, the prompt names the built-in worker agent, which runs the edit in its own thread.

Have the worker agent add a --parallel mode with Parallel.ForEach,
MaxDegreeOfParallelism from a --threads option (default
Environment.ProcessorCount), and a new QrReader per file. Use the synchronous
Read inside the loop body, since Parallel.ForEach does not await an async
lambda. Write out/parallel.csv sorted by file name, then compare its status
and value columns with out/sequential.csv and fix any row that differs
before you finish. Append mode, thread count, and wall time to out/runs.csv.
Text

An async lambda passed to Parallel.ForEach compiles as async void, so the loop returns before the reads finish and the results file comes out short. The comparison with the sequential file catches it.

How do you limit concurrent reads with ReadAsync?

A parallel loop blocks a thread per read. ReadAsync behind a SemaphoreSlim keeps a fixed number of reads in flight and leaves the calling thread free, the pattern the async guide gives for high-volume batches. It takes an IQrInput and has no overload for a file path, so each task still opens its own bitmap.

Add a --pipeline mode: Task.WhenAll over every file, each task waiting on a
SemaphoreSlim sized by --concurrency and awaiting ReadAsync. Run it at
concurrency 2, 8, and Environment.ProcessorCount, writing
out/pipeline-c<N>.csv for each and appending to out/runs.csv. Confirm each
file matches out/sequential.csv row for row, then tell me which setting was
fastest according to runs.csv.
Text

The detection model already spreads one read across several cores, so a higher limit need not run faster. Let runs.csv pick the limit for your hardware, and keep the semaphore for what it does reliably, which is capping memory use.

Which scan mode should a batch use?

The detection model reads photos the basic pass misses, and it also accounts for most of the time each read takes.

Measure what the machine learning detector costs on this backlog. Run the
pipeline at the fastest concurrency from runs.csv, first under
QrScanMode.Auto and then under QrScanMode.OnlyBasicScan, writing
out/mode-auto.csv and out/mode-basic.csv with per-image milliseconds. Join
both with the category column of Backlog/manifest.csv and report decoded
count and median milliseconds per category for each mode. If either mode
reads files the other misses, keep Auto as the default and build a single
OnlyBasicScan retry of its Empty files into the decoder for every later run.
Rerun to out/mode-fallback.csv and count the recoveries.
Text

Both modes read every clean scan and fax copy, and each missed a different handful of phone photos. On the photos only the basic pass read, the detection model located the code and could not decode it, so Auto never fell back. Retrying the empty files with QrScanMode.OnlyBasicScan recovered every photo.

How do you validate decoded values and report failures?

A batch meets three kinds of failure, and each one looks different in code.

InputResultStatus
Empty, truncated, or non-image fileThe image load throws before IronQR runsUnreadable
No code, or damage past error correctionAn empty collection, never nullEmpty
Any code, read in the detection-only modeCorner points, an empty Value, a QrType of QrEncoding.NoneEmpty, not Decoded

The third row is the dangerous one. Our first sequential decoder would have counted it as Decoded, because it only checked whether any result came back.

Backlog/README.md defines a valid asset tag. Add Services/TagValidator.cs and
apply it after each read: a decode only counts when the symbol is a standard
QR code (QrEncoding.QRCode), the value matches the documented pattern, and
the Luhn check digit holds. Give the others the statuses WrongSymbol,
InvalidFormat, or BadCheckDigit. Rerun the pipeline to out/validated.csv and
list every file whose status disagrees with expected_status in the manifest.
Text

A check digit is part of your data, so IronQR cannot validate it. Reed-Solomon correction inside the read, which the checksum and fault tolerance guide describes, only proves the symbol decoded, and a result carries no confidence property. A tag with a wrong check digit scans cleanly and is still invalid.

IronQR's error messages guide shows the per-file logging pattern the next prompt applies to the whole backlog.

Every file that is not Decoded needs a recorded reason. Write one JSON line
per file to out/decode-log.jsonl (file, status, exception type, message,
milliseconds), send IronQR's own log to out/ironqr-debug.log through
IronQr.Logging.Logger, and generate out/failures.md from the JSONL only: one
section per status other than Decoded, a count, and every file name. Then
check the section counts against out/validated.csv.
Text

In version 2026.10.1, the IronQr.Logging.Logger log records the scan path each read took, such as a detection scan with the machine learning model. Because the report is built only from the JSONL, a failure the decoder never logged shows up as a count that disagrees with the results file.

How do you diagnose an image that will not decode?

Codex can look at a picture. Attach the file with codex --image when you start the session, or paste it into the composer.

I attached IMG_0018.png from Backlog/images/ with codex --image. The pipeline
marks it Empty. Look at the picture and tell me whether it is a damaged code,
a page with no code, or a capture problem a rescan would fix. Then test your
answer: read it with all three QrScanMode values and write
out/retry-IMG_0018.csv. Do not change the pipeline.
Text

In our run, all three modes came back empty for this file, and in calibration the detection model found nothing to locate. The code itself is damaged, so the loss is past what error correction recovers, and neither another read nor a rescan of the same label will change that. The fix is a replacement label and, if you print the labels yourself, a higher error correction level.

How do you debug incorrect batch output end to end?

Send the explorer agent into Jobs/NightlyImport.cs and Logs/nightly-2026-10-06/
before anything is edited. That run reported six unreadable files and no other
failure, yet Backlog/archive-index-gaps.csv lists assets the archive never
received. Find what the missing assets have in common, explain why the job
counted them as successes, and propose the smallest fix together with the
command that would prove it. Put the findings in out/nightly-diagnosis.md and
change no code.
Text

Approve the diagnosis before any fix. A good one quotes the line that decides success. Check the proposed fix against out/mode-fallback.csv too.

How do you turn one-off code into reusable, tested components?

Put the worker agent on folding the modes into a BacklogJob class. Add the
Microsoft.Extensions.DependencyInjection package, register the job with it,
and configure it with a BacklogOptions record (input folder, concurrency,
scan mode, output folder). Keep the validator, the basic-scan retry, and the
logging in it, stay under 160 lines, and add XML doc comments. Before
finishing, run it over Backlog/images/ into out/final.csv and confirm every
row agrees with the manifest's expected_status and expected_value.
Text

A refactor can drop the validator, the basic-scan retry, or the per-file reader without a build error. Only a full rerun against ground truth catches that.

Add an xUnit project, AssetBacklog.Tests, and cover BacklogJob with tests
that read real files from Backlog/images/: a clean scan decodes to its
manifest value, the zero-byte file comes back Unreadable without throwing, a
damaged image is Empty, a Micro QR label is WrongSymbol, a bad check digit is
BadCheckDigit, and a parallel run over 40 files matches a sequential one.
Assert on status and value. Run dotnet test and report the totals it prints.
Text

The manifest fixes status and value, while timings change from run to run.

Keep recurring IronQR questions in the repository skill. OpenAI's custom prompts page marks the older ~/.codex/prompts folder deprecated in favor of skills.

$ironqr Using the IronQR API reference, list every member of QrResult and say
whether IronQR reports a confidence or checksum score for a read. Then point
to the line in BacklogJob that decides a file is Decoded.
Text

In our run, Codex listed the four members of a result and traced Decoded from the job through the decoder to the validator line that returns it.

How do you reuse these prompts on another image folder?

Change the paths, and every prompt above runs as written. The AI coding assistants guide runs a single acceptance prompt end to end and publishes the result.

What did the prompts produce in a real run?

We sent every prompt above to Codex in order, in one thread, against a sample repository. It holds a console application and a backlog of 301 images (clean scans, phone photos, fax copies, damaged labels, blank cover sheets, Micro QR shelf labels, invalid tags, and six files that are not images at all), with a manifest of the expected status and value of each. After the session we rebuilt the project in Release and ran the tests. We also decoded every image independently to check the results below.

  • The basic-scan retry raised Decoded from 256 to 264. Before it, each mode decoded 256 of 301, and 16 files differed between the two.
  • A clean scan took a median 360 ms under Auto and 35 ms under the basic scan, which ran the whole backlog in 7 s at limit 2.
  • The sequential decoder recorded 256 Decoded, 39 Empty and 6 Unreadable in 71 s.
  • Parallel mode wrote the same 301 rows in 60 s at 32 threads, and the pipeline wrote them in 55, 66 and 68 s at limits 2, 8 and 32.
  • In the quickstart, 9 of 12 rows decoded, 2 came back empty and 1 zero-byte file threw.
  • Validation matched the manifest with 240 Decoded, 31 Empty, 8 WrongSymbol, 8 InvalidFormat, 8 BadCheckDigit and 6 Unreadable.
  • The failure report held 61 records, one per failed file.
  • After the refactor, the 64-line job matched the manifest on 301 of 301 rows, and 6 of 6 tests passed, again in Release.
  • All 16 prompts ran with every build passing and no manual code edits. Prompts 13 to 16 hit the free-plan usage limit and resumed in the same thread after Codex was signed in again.
  • Before the investigation prompt we added a nightly job with a planted fault, its logs and a gaps list. Codex found the detection-only mode on photo JPEGs and a success check that accepts any result.

Codex's version, model, timing and test machine for this run are in the shared run record.


Troubleshooting


Questions?

If you have any questions, reach out to support@ironsoftware.com

Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...
Read More

Ready to Get Started?

Nuget Downloads 76,271Version:2026.10just released

Get your free 30-day Trial Key instantly.
No credit card or account creation required
C# NuGet Library for PDF
Install with NuGet

Version: 2026.10

PM > Install-Package IronQR
nuget.org/packages/IronQR/
  1. In Solution Explorer, right-click References, Manage NuGet Packages
  2. Select Browse and search "IronQR"
  3. Select the package and install
C# PDF DLL
Download DLL

Version: 2026.10

  1. Download and unzip IronQR to a location such as ~/Libs within your Solution directory
  2. In Visual Studio Solution Explorer, right click References. Select Browse, "IronQR.dll"

Licenses from $999

Key in blue circle

Get your free 30-day Trial Key instantly

Your trial license will be sent to your email address

No limitations. 100% unlocked. No credit card.

bullet_checkedNo credit card or account creation requiredNo limitations. 100% unlocked. No credit card.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronQR
Book your free Live Demo
Booking Badge

Trusted by Millions of Engineers Worldwide

Iron Software's customer logos
Get a Quote, No Obligation
Your details will always be kept confidential.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Trusted by Millions of Engineers Worldwide
Get your free 30-day Trial Key instantly.
No credit card or account creation required
C# NuGet Library for PDF
Install with NuGet

Version: 2026.10

PM > Install-Package IronQR
nuget.org/packages/IronQR/
  1. In Solution Explorer, right-click References, Manage NuGet Packages
  2. Select Browse and search "IronQR"
  3. Select the package and install
C# PDF DLL
Download DLL

Version: 2026.10

  1. Download and unzip IronQR to a location such as ~/Libs within your Solution directory
  2. In Visual Studio Solution Explorer, right click References. Select Browse, "IronQR.dll"

Licenses from $999