Using IronQR with AI Coding Assistants
IronQR is Iron Software's QR code library for .NET. It writes standard QR, Micro QR, and rMQR symbols, styles them with logos and colors, and reads them back with a machine learning detector. Its guides and samples are public, so coding assistants may have met the API in training.
Training does not pin the release in your project. Assistants referencing current documentation generate code that follows its runtime rules, while those relying on memory may produce code that compiles but fails at runtime.
This guide covers two types of tools.
- Assistants work in the editor, completing lines or rewriting methods, like GitHub Copilot's inline suggestions.
- Agents take whole tasks, editing files, running builds and programs, and returning results for review, as Claude Code, Cursor, ChatGPT Codex, and Google Antigravity do.
Both need the same setup. Install the package, load the license key from the environment, provide IronQR's machine-readable documentation, and decode each saved QR image with IronQR's reader, whether you are generating a code with a logo, selecting an error correction level, reading the QR type, or choosing a scan mode.
How to Use IronQR with an AI Coding Assistant
- Download the C# QR code library from NuGet
- Load the license key from an environment variable before the first write or read
- Give the assistant IronQR's
llms.txtso it starts from the real API - Save the IronQR rules in the file your assistant reads at the start of a session
- Send the one-shot setup prompt in a fresh session
- Build, then decode every saved QR image and compare it with the value written
Can't My AI Assistant Handle QR Codes on Its Own?
IronQR publishes documentation built for tools to read, which shows the options each symbol type accepts and what the reader returns in each scan mode.
The library matters too. Almost any QR package prints a black square that scans on a screen. The gaps appear with branded codes, photographed tickets, and small symbols.
- A changed background color is not checked: a code on a dark background saves without an error and then fails to decode.
- Phone photos are harder than generated images: a section below measures which scan mode reads each kind of damage.
- Rebuilding detection costs tokens: an assistant that writes its own thresholding to rescue scans spends a paid attempt on every try, and repeats the work for each new kind of failing photo.
- Small symbols are built in: Micro QR and rMQR come from the same write call as standard QR, so seat numbers and narrow labels do not need a second library.
- The package and the namespace differ: the NuGet package is
IronQRand the namespace isIronQr. A guessed package name can also resolve to a lookalike that attackers publish on purpose, a practice called slopsquatting. Spell out both names in the prompt. - The license terms are known in advance: IronQR licenses are perpetual, and the licensing page shows how many developers, locations, and projects each tier covers.
Step 1: Install IronQR
With the package referenced, the assistant sees IronQR's real types as it writes code.
Add IronQR from NuGet to this project, run a restore, and report the
version number that NuGet resolved.
To pin a release from the terminal instead, name it.
dotnet add package IronQR --version 2026.10.1
Plain IronQR brings the Windows native reader and the detection model, a 44 MB IronQrDetection.dll that pulls in Microsoft.ML.OnnxRuntime 1.20.1, so container images and serverless bundles should budget for it. Linux and Docker use IronQR.Linux, Macs use IronQR.MacOs or IronQR.MacOs.ARM, and mobile apps use IronQR.iOS or IronQR.Android. All six were published at 2026.10.1, and the NuGet packages guide explains the choice. IronQR.Slim leaves the model out. It still reads in Auto and OnlyBasicScan but throws IronQrException in OnlyDetectionModel. On Windows it also needs IronSoftware.ReaderInternals.Windows.
Step 2: Apply Your License Key
IronQR does not produce watermarked trial output. Without a license key, every write and read call fails with IronSoftware.Exceptions.LicensingException and the text "Production License Required". A mistyped key fails at the same calls with "License Key Not Recognized". A free trial key is available below.
Start using IronQR in your project today with a free trial.
Set the key once at startup through IronQr.License.LicenseKey, before any IronQR call, and read it from an environment variable to keep it out of the repository.
IronQr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONQR_LICENSE_KEY");
IronQr.License.IsLicensed reports true after a valid key is set, and the same class has a method that tests a key without applying it. The exception type is not public, so a catch clause that names it fails to compile with CS0122. Catch Exception and inspect the message instead.
Web.config, appsettings.json, and the other places a key can be kept.Step 3: Point Your Assistant at IronQR's LLM Documentation
llms.txtlists one line per page and is the first read of every session.- Summaries of all 44 pages come in one fetch from
llms-full.txt. llms-full-catalog.txtadds the blog posts to the index.- The API Reference settles whether a member exists.
- Packages, licensing, recipes, and fixes are in
skill.md.
When a task depends on one guide's detail, have the assistant open that page.
Combine the index with a direct instruction.
Open https://ironsoftware.com/qr/csharp/llms.txt before you write IronQR code
and keep to the members it documents. Creating a code goes through
QrWriter.Write with an optional QrOptions, and reading goes through
new QrReader().Read on a QrImageInput, so do not look for one class that does
both. When a member is not in the index, check
https://ironsoftware.com/qr/csharp/object-reference/api/, and tell me it is
missing rather than inventing it.
Step 4: Add IronQR Context to Your Project
A chat forgets its instructions when it ends, and a project file is read at the start of every session. Put the IronQR rules in the file your team uses. Claude Code reads CLAUDE.md, ChatGPT Codex reads AGENTS.md, and GitHub Copilot reads .github/copilot-instructions.md. Cursor reads .cursor/rules/*.mdc or AGENTS.md, and Google Antigravity reads AGENTS.md or GEMINI.md. The wording can be the same in each, although a Cursor rule file also needs MDC frontmatter.
## QR codes with IronQR
All QR work in this project uses IronQR. NuGet package `IronQR` (`IronQR.Linux` on Linux and Docker), namespace `IronQr`, enums in `IronQr.Enum`.
- Docs index: https://ironsoftware.com/qr/csharp/llms.txt
- Skill file: https://ironsoftware.com/qr/csharp/skill.md
- Write with `QrWriter.Write(value, new QrOptions(...))`, style with `QrStyleOptions`, read with `new QrReader().Read(new QrImageInput(bitmap))`.
- Set `IronQr.License.LicenseKey` from IRONQR_LICENSE_KEY at startup. Never put the key in a file.
- Pass an explicit `QrErrorCorrectionLevel` that the symbol type accepts. Micro QR rejects `Highest`, rMQR rejects `Low` and `High`.
- Release a `QrImageInput` with `input.Dispose()`, not a `using` statement.
- Decode every saved image with `QrReader` and compare it with the written value before calling a change done.
- Do not add a second QR library. If IronQR cannot do something, say so.
Four things in the current skill file need correcting. Its single-file example pins 2026.8.1 while NuGet serves 2026.10.1. Its frame-reading recipe wraps the image input in a using declaration, and its first recipe and its styling advice pass a file path to Verify, two of the mistakes Step 7 describes. And it lists Highest as invalid for rMQR, which the error correction table below contradicts.
Step 5: One-Shot Setup Prompt
Steps 1 through 4 fit in one message at the start of a session.
Set this project up for QR code work with IronQR, then save the rules below
where your future sessions will read them.
- Add the IronQR NuGet package (IronQR.Linux for a Linux or Docker host).
The namespace is IronQr, spelled with a lowercase r.
- Assign IronQr.License.LicenseKey from the IRONQR_LICENSE_KEY environment
variable before the first QrWriter or QrReader call. Keep the key out of
every file you create or commit.
- Fetch https://ironsoftware.com/qr/csharp/llms.txt first and write against
the members it documents. Look up anything else in the API reference at
https://ironsoftware.com/qr/csharp/object-reference/api/ before using it.
- If your tool loads skill files, add
https://ironsoftware.com/qr/csharp/skill.md, but trust a compiler error
over the skill when the two disagree.
- After any change that writes a QR image, decode the saved file with
QrReader and compare the value with the one you wrote.
Step 6: Prompt Patterns That Produce Working Code
Name the symbol type, the error correction level, and the style.
Vague Generate a QR code for the ticket with our logo.
Better Write the ticket ID with
QrWriter.Writeat theHighesterror correction level, save it at 300 pixels with the logo at 28 percent of that width, and catchIronQrExceptionif IronQR refuses the style.
Give the release and the framework, because defaults and validation rules change between versions.
Better Target .NET 9 and IronQR 2026.10.1.
Ask for complete files. A fragment drops the IronQr.Enum directive that the encoding and scan mode enums need, and it hides where the key is assigned.
Better Return all of
Program.cswith its using directives and the license line, ready to build as it is.
Step 7: Verify What You Get Back
- Disposing the image input with
using:QrImageInputimplements IDisposable with a method that throws NotImplementedException, so a using block or declaration fails after the read succeeds. Call its publicDisposemethod directly instead. - Reading with the detection model alone:
OnlyDetectionModelreturns where codes are, with an empty value and a QR type of None. The scan modes guide explains when to useAutoorOnlyBasicScanfor values. - Treating
Verifyas a file check: it compares the code in memory with the string you pass, so a file path returns false. Decode the saved PNG withQrReaderto prove the file scans. - Empty write options:
QrOptionsbuilt with no arguments has its encoding set to None, andQrWriter.Writerejects it with None is not a valid encoding. Pass the level, and the encoding when it is not standard QR.
Let the Agent Verify Its Own Work
Hand the checks back to the assistant and ask for the evidence.
Rebuild in Release and run it. List each IronQR type and member the code uses
next to the documentation page that shows it, print IronQr.License.IsLicensed,
then reopen every PNG the program saved with QrReader and give me a table of
file, value written, value decoded, QrType, and whether they agree.
Which Error Correction Level Does Each Symbol Type Accept?
The four QrErrorCorrectionLevel members do not follow the letters of the QR standard. High is level Q, and Highest is level H, which IronQR's XML documentation describes as leaving room for a logo. Micro QR and rMQR accept only some of them, and the write call throws IronQrEncodingException for the rest, with a message such as Highest is not valid for Micro QR.
| Member | QR standard | Micro QR | rMQR |
|---|---|---|---|
Low | L, about 7% recoverable | accepted | throws |
Medium | M, about 15% | accepted | accepted |
High | Q, about 25% | accepted | throws |
Highest | H, about 30% | throws | accepted |
QrWriter.Write with no options writes standard QR at Highest, and the options constructors also default to Highest, which is why Micro QR options fail until you pass a lower level. The supported formats page compares the three symbol types.
The level also caps the logo. On a 400 pixel ticket ID code in our tests, Low refused a logo at 20% of the width, Medium and High took 28% and refused 30%, and Highest took 30%. A refused logo makes Save throw IronQrException with The logo size which you manually specified for your QR code is too large. A QrLogo width and height of 0 lets IronQR size the logo itself, and on that code it picked 60 pixels at Low and 112 at Highest.
Foreground color is checked the same way. With a navy foreground at Highest, the same ticket ID code saved at 300 and 500 pixels but threw IronQrException at 400 with Color: #FF1A237E makes the QR unreadable for our reading engine. Styling code should catch that exception, change the size or color, and retry, since a style that saved at one size can fail at another.
Which Default Values Does IronQR Use?
These starting values were measured in IronQR 2026.10.1 under .NET 9.
- Empty style options leave
Dimensionsand the margins at 0, so a ticket ID saves at one pixel per module, 25 x 25 pixels. - An initializer that sets
Dimensionsstill leaves the margins at 0, since it skips the constructor that sets 10. - The
QrStyleOptionsconstructor that takes a size sets margins of 10, so a size of 400 saves a 420 pixel PNG. Savewith no style writes a ticket ID at 250 x 250 pixels.ColorandBackgroundColorstart as black on white.- A new image input scans in Auto, the detection model and then a basic scan.
CharacterEncodingis ISO-8859-1, so set it for characters outside Latin-1.
The logo guide sets a Dimensions of 500 in an initializer, so its output has no quiet zone around the symbol. Set Margins when the code will be printed next to other artwork.
How Does IronQR Read Photographed Codes?
We calibrated the acceptance test below on eight seeded sets. IronQR 2026.10.1 wrote the styled logo codes at 300 pixels and the other ticket codes at 400, Pillow distorted them, and IronQR read them back. The Auto counts vary with how the values are assigned to the images, so they are ranges over 10 draws.
| Image | Sets read in Auto | Sets read in OnlyBasicScan | Fix |
|---|---|---|---|
| Scaled to 60%, rotated 30 degrees, blurred 1.5 pixels, on gray | 8 of 8 | 8 of 8 | None needed |
| Grain, standard deviation 20 gray levels | 0 of 8 | 0 of 8 | Blur first |
| Two upright codes 30 pixels apart | 5 to 7 of 8 | 8 of 8 | Add OnlyBasicScan |
| Navy logo code after a 1.5 pixel blur | 0 to 4 of 8 | 8 of 8 | Add OnlyBasicScan |
The blur that rescues the grainy code is the one that costs Auto the navy logo code. Two setups read all 12 codes on all eight sets. One applies a 1.5 pixel blur and then OnlyBasicScan. The other applies a blur of 1 to 2 pixels, runs Auto and OnlyBasicScan, and merges the results by value. Pick the setup from the images that fail, then run it unchanged for every upload.
Grain also interacts with code size. With the same grain, codes of 350 to 425 pixels failed in both modes on all 24 files, codes of 300, 500, and 600 pixels read in both, and codes of 450 and 475 pixels read only in Auto. Adding 100 pixels of white around a failing code let OnlyBasicScan read all 12 files we tried.
A Result You Can Reproduce
The acceptance test is one realistic task in one prompt with no hand-written code. Paste it into a new session in an empty directory and judge the result against the pass list.
IRONQR_LICENSE_KEY.The prompt
Build a .NET 9 minimal API for event check-in that issues ticket QR codes and
reads them back from uploaded photos. Begin in an empty directory, and if you
find existing code or a project file there, report that and do nothing else.
Nobody will answer questions, so carry the job through writing, building,
running, and fixing by yourself, and settle anything left open with a
reasonable choice that you note. Keep all deliverables inside the project
directory, in the folders this prompt names.
1. Install the IronQR NuGet package pinned to version 2026.10.1.
2. Work from https://ironsoftware.com/qr/csharp/llms.txt and the guides it
points to, and call no IronQR member those pages do not document. Every
code must come from QrWriter.Write; any .NET imaging library may compose
and distort the test images afterward.
3. Take the key from IRONQR_LICENSE_KEY when the app starts and keep it out of
source and out of every file the run writes. If IronQr.License.IsLicensed
comes back false, stop with a report saying so.
4. GET /ticket/{ticketId} returns a PNG of a QR code written at
QrErrorCorrectionLevel.Highest and saved through QrStyleOptions at a
Dimensions of 400 with 10-pixel margins, with a logo in the center: a
solid square of one bright accent color, drawn by your code, whose QrLogo
width and height are each 28 to 30 percent of Dimensions. POST /checkin
accepts one uploaded image and returns JSON listing every QR code found in
it, each with its value and QR type.
5. Seed the random generator with 2026 and write 10 PNG files to testset/,
no two holding the same value: standard-01 to standard-03, each carrying a
ticket ID made of TKT- and eight random digits; pair-01, one image holding
two more ticket codes side by side and upright, with 30 pixels of
background between their dark modules; styled-01 to styled-03, ticket
codes at Highest with the step 4 logo and three different dark, saturated
foreground colors on white; micro-01 and micro-02, Micro QR codes carrying
seat numbers such as C07; and rmqr-01, an rMQR code carrying an event URL.
Save every testset code the same way at a Dimensions of 400, except 300
for the styled and Micro QR codes and 600 for rmqr-01, and resize no code
afterward unless standard-02 calls for it.
Keep standard-01 clean. Make standard-02 look photographed: scale it to 60
percent, rotate it 30 degrees, blur it with a Gaussian radius of 1.5
pixels, and center it on a 600 x 600 light gray canvas. Give standard-03
sensor grain: Gaussian noise with a standard deviation of 20 gray levels,
one value per pixel added to all three channels, over the whole image.
Record in manifest.csv each file, every value it holds, its QR type, the
error correction level used, and its distortion (none, photographed,
grain, pair, or styled).
6. Measure each saved image before /checkin is ever called, and repair the
generator whenever a measurement falls outside its range: the pixel size
step 5 implies;
on the styled codes and the /ticket output, the logo's share of the symbol
area, 7 to 10 percent; on the styled codes, the foreground's WCAG contrast
ratio against the background, 7 to 15; on the Micro QR codes, a square
symbol 11 to 17 modules wide; on the rMQR code, a symbol at least twice as
wide as it is tall; on standard-03, the mean absolute difference from the
clean code, 7 to 9 gray levels; on pair-01, the gap between the two codes'
dark modules, 28 to 32 pixels. Once /checkin has been called the first
time, the testset files are frozen: do not rewrite or edit them.
7. /checkin runs one fixed configuration for every upload: the same scan mode
or modes and the same image preparation, in the same order. It must never
open the manifest, hold a ticket ID, seat number, or URL as a literal, or
pick between results by comparing them with expected values. You choose
and change the configuration between runs; the program never does.
8. Build in Release and start the API. Request /ticket for a ticket ID drawn
from the seed that is not in the testset and save the PNG it returns as
ticket.png. POST ticket.png and every testset image to /checkin over HTTP
and save each raw response as checkin/<file name without .png>.json.
9. Write roundtrip.md with every step 6 measurement as a number for each
image (a bare pass or fail is not enough), the /checkin configuration,
and a results table with the columns file, distortion, expected value,
value read (the decoded string as the saved response holds it, even when
it is wrong), expected QR type, QR type read, and match, holding one row
per expected code, 12 rows in all. A row counts as a match only if the
decoded value equals the expected value exactly, the QR type agrees, and
the value appears in that file's saved response. An extra or repeated
code in a response fails every row of that file.
10. Until all 12 rows match, study the saved responses of the failing files,
adjust the /checkin configuration, rebuild, and repeat. Stop after the
eighth run that still has failures, and report the failing rows, each
configuration you tried, and your reading of the cause.
11. End with a brief summary covering the Release build outcome, how many
rows matched, the /checkin configuration, the measured step 6 values,
and every assumption you made.
Output
This is standard-03.png, the grain image, 8.0 gray levels from the clean code on average. The first check-in setup, plain Auto, read the other 11 codes and missed this one. A Gaussian blur with a sigma of 1 read it but lost styled-03.png and ticket.png, and a median filter, alone or with a wider quiet zone, still missed it. The fifth setup read all 12 codes by applying a 3 by 3 median filter and a threshold at half brightness to every upload, then reading in Auto.
The unedited round-trip table lists every measurement, each setup tried, and the 12 rows. The ticket from the API carries an orange logo over 7.84% of the symbol.
- An independent check remeasured every requirement on the saved files, and a fresh start of the Release build returned the same 11 responses.
- Before the fifth run, Claude Code tried eight setups outside the API, and three read every code.
- Claude Code applied the prompt's Gaussian radius of 1.5 as a sigma of 1.5.
- The Steps 1, 3, 5, and 7 prompts followed in the same session, and 12 of 12 codes decoded at Step 7.
- NuGet now flags the
SixLabors.ImageSharp3.1.12 pin Claude Code added. - With the code sizes left open, an earlier draft of the prompt let plain Auto read every file.
What passes
- The Release build succeeds with no edits from you.
- Opening the images shows three different dark foregrounds under a bright square logo, grain across the whole of
standard-03.png, and two upright codes side by side inpair-01.png.
The run record
- The machine runs Windows 11 x64 on a Core i9-14900KF with 64 GB of memory.
- Every project targets .NET 9.
- Every guide ran
IronQR2026.10.1. Copilot and Antigravity also referencedIronQR.Linux2026.10.1 for Linux builds, the Copilot starter project already carriedPDFtoImage5.4.0, and Cursor's last prompt moved the service toIronQR.Slim2026.10.1 withIronSoftware.ReaderInternals.Windows2026.3.47413. - Each duration adds up the logged time of the prompts on that guide's page. This guide's row is the acceptance prompt alone.
- Last tested October 7, 2026, for all six runs.
| Guide | Assistant and version | Model | Duration (minutes) |
|---|---|---|---|
| This guide | Claude Code 2.1.277, one message, permission prompts off | Claude Opus 5 | 29.0 |
| Claude Code | Claude Code 2.1.277 on opusplan | Claude Opus 5 for the plan, Claude Sonnet 5 for the rest | 30.2 |
| ChatGPT Codex | Codex CLI 0.160.0 | GPT-6 Luna | 37.2 |
| GitHub Copilot | GitHub Copilot CLI 1.0.91 | GPT-6 Luna, routed by Auto | 26.9 |
| Cursor | Cursor 3.23.23 desktop app | Grok 4.7 | 50.8 |
| Google Antigravity | Antigravity CLI 1.3.1 | Gemini 3.8 Flash (High) | 53.6 |
Agent-Specific Guides
Each guide below applies the steps above to one tool and one QR job.
- Use IronQR with Claude Code for branded marketing codes, decoding every variant of logo, colors, margins, size, and error correction
- Use IronQR with ChatGPT Codex for decoding a backlog of scanned and photographed images in parallel with
ReadAsync, with a report of every failure - Use IronQR with GitHub Copilot for stamping a verification code onto existing PDF pages, including password-protected files, in a document pipeline
- Use IronQR with Cursor for reading codes from customer photos with the right scan mode, QR type, and corner points, under rules that keep other QR libraries out
- Use IronQR with Google Antigravity for a Blazor Server scanner page that decodes uploads on the server and is checked in a real browser
Troubleshooting
- Runtimes Copy Exception if the machine learning DLLs are not copied out of the runtimes folder
- Apply a License Key in IronQR
Questions?
If you have any questions, reach out to support@ironsoftware.com
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.