Using IronWord with AI Coding Assistants

Curtis Chau
Curtis Chau
Updated: October 19, 2026

IronWord is Iron Software's DOCX library for .NET. It allows you to create, edit, style, and build tables in Word documents, and read text without Microsoft Office. Available on NuGet since December 2023, IronWord includes public guides and code examples.

Current examples do not reflect the behavior of release 2026.10.4. In this version, text element size, color, and weight are read-only. Document-wide replace does not affect table cells, and document-wide text extraction excludes tables.

There are two types of AI coding tools.

  • Assistants operate within the editor. They complete lines or rewrite methods, similar to GitHub Copilot's inline suggestions.
  • Agents manage complete tasks, such as editing multiple files, running builds and programs, and returning results for review, as seen with Claude Code, Cursor, ChatGPT Codex, and Google Antigravity.

Both types need the same setup, whether the task is adding text, styling, building tables, replacing placeholders, or extracting text. Install the IronWord package, read the key from an environment variable, give the assistant IronWord's machine-readable documentation, and reopen each saved DOCX before the task counts as done.

Claude Code logoChatGPT Codex logoGitHub Copilot logoCursor editor logoGoogle Antigravity logo

Can't My AI Assistant Handle Word Documents on Its Own?

Without documentation, an assistant writes IronWord code from training data that also covers other DOCX libraries and older IronWord releases. IronWord's machine-readable documentation gives it the current API.

A one-page letter is simple. Differences between DOCX libraries appear with templates, tables, and large-scale generation.

  • A faulty document saves without an error: IronWord raises no exception when a line merges into the previous paragraph or a placeholder stays in a table cell. Only reading the saved file back reveals the fault.
  • Raw OpenXML requires low-level code for every feature: runs, run properties, tables, and section settings are separate XML elements, and the OpenXML SDK has a class for each, so an assistant writes long code that you then maintain. IronWord exposes them through one object model.
  • Package and namespace share one name: both are IronWord, with the model types under IronWord.Models. Registering package names that assistants hallucinate is called slopsquatting, so the prompt should give the exact package name.
  • Licensing terms are defined in advance: an IronWord license is perpetual, and the licensing page gives each tier's limit on developers, locations, and projects.

Step 1: Install IronWord

Ask the assistant to install the package.

Install IronWord 2026.10.4 from NuGet in this project and restore it.
Then tell me the IronWord version the restore picked.
Text

From a terminal, pin a release.

dotnet add package IronWord --version 2026.10.4
SHELL

A single package supports Windows, Linux, and macOS, as IronWord 2026.10.4 includes native layout engines for win-x64, win-x86, linux-x64, osx-x64, and osx-arm64. The Docker setup guide recommends Ubuntu, Debian, or RHEL base images and covers installing fonts in Linux containers.


Step 2: Apply Your License Key

An unlicensed program can still create a document, load one, extract its text, and replace words in memory, but every save throws IronSoftware.Exceptions.LicensingException with the message Production License Required. This covers SaveAs and every form of Save, to a path, a stream, or bytes. A mistyped key fails the save with License Key Not Recognized. A free trial key can be requested below.

Start using IronWord in your project today with a free trial.

First Step:
arrow pointer

Set the key once at startup, before any IronWord call. Reading it from an environment variable keeps it out of the repository.

IronWord.License.LicenseKey = Environment.GetEnvironmentVariable("IRONWORD_LICENSE_KEY");
C#

IronWord.License.IsLicensed is true when the assigned key is valid, and IronWord.License.IsValidLicense tests a key string. The exception class is not public, so catch Exception and read its message.

Please note: IronWord License Keys explains the Web.config and appsettings.json options as well.

Step 3: Point Your Assistant at IronWord's LLM Documentation

  • llms.txt has a one-line entry per page. Give it to the assistant when each session starts.
  • llms-full.txt puts a summary of all 46 documentation pages in one file.
  • llms-full-catalog.txt adds blog articles to the index.
  • The API Reference gives each member's declaration.
  • skill.md is for tools that load skill files. It covers installation, licensing, code recipes, and a table of common failures.
  • The AI Product Fact Sheet names the supported .NET targets and the main use cases.

Name a specific guide when a task relies on one.

Read https://ironsoftware.com/word/csharp/llms.txt before you touch IronWord code.
Use only what those pages show.

Here's how the object model works:
- A document is a WordDocument made of paragraphs, runs, and tables.
- Styling goes on the run through a TextStyle.
- Nothing reaches disk until the document is saved.

If you need a member the pages don't mention, look it up in the API reference.
It's at https://ironsoftware.com/word/csharp/object-reference/api/.
Build once to prove the call compiles.
If it still looks doubtful, tell me so instead of guessing.
Text

With permission to answer "not documented", the assistant has a choice other than making up a method.


Step 4: Add IronWord Context to Your Project

Instructions typed in chat are gone when the session ends, so keep the IronWord rules in the project file your tool loads. That file is CLAUDE.md for Claude Code, AGENTS.md for ChatGPT Codex, .github/copilot-instructions.md for GitHub Copilot, an .mdc file in .cursor/rules/ or AGENTS.md for Cursor, and AGENTS.md or GEMINI.md for Google Antigravity. One block of text works for all of them, though Cursor rule files also take MDC frontmatter.

All DOCX work in this project uses IronWord.
The NuGet package and its namespace are both `IronWord`.
Model types live in `IronWord.Models`.

Follow these rules for every Word document:
- Start from the docs index at https://ironsoftware.com/word/csharp/llms.txt.
- Load the skill file from https://ironsoftware.com/word/csharp/skill.md.
- Build documents from `Paragraph`, `Run`, and `Table` objects.
- Add each one with `AddParagraph` or `AddTable`.
- `WordDocument.AddText` appends to the last paragraph rather than starting a new one.
- Style a run with `Run.Style = new TextStyle { ... }`.
- Give the style a `TextFont` whenever it sets `FontSize`.
- Size, color, and weight on `TextContent` are read-only.
- `WordDocument.ReplaceText` does not reach table cells.
- Replace placeholders paragraph by paragraph across `doc.Paragraphs`.
- That list includes the paragraphs inside table cells.
- Read body text from top-level paragraphs.
- Read table text from `Tables`, `Rows`, and `Cells`, and cast each cell to `TableCell`.
- `WordDocument.ExtractText` leaves out tables and joins paragraphs without a separator.
- Build a new `WordDocument` for each file you save. A second save writes the body again.
- Set `IronWord.License.LicenseKey` from IRONWORD_LICENSE_KEY at startup.
- Never print the key or write it into a file.
- Reopen every saved document before calling a change done.
- Compare its paragraphs and cells with what was written.
- Do not add a second DOCX library. If IronWord cannot do something, say so.
Markdown

The skill file disagrees with the measurements in at least seven places. Its single-file example still uses 2026.8.1, three releases before the 2026.10.4 package on NuGet. It refers to document-wide replace as a whole-document find and replace, which the measurements below contradict. Its examples serialize the document model to JSON, which caused a serialization error on every document tested in 2026.10.4. They present the parameterless Save only as a way to get the bytes, although IronWord.xml states that it also writes a loaded document back to its source file. The skill file styles text through TextContent.Style, which saved an empty run style. Step 7 covers the other two, the document's add-text call and the shipped XML.


Step 5: One-Shot Setup Prompt

The prompt below combines Steps 1 to 4 in a single opening message.

Prepare this project for Word document work with IronWord.
Then append these rules to the instruction file your future sessions load:
- Reference the IronWord NuGet package at version 2026.10.4.
- Its namespace is also IronWord.
- The paragraph, run, table, and style types live in IronWord.Models.
- Take IronWord.License.LicenseKey from the IRONWORD_LICENSE_KEY environment variable.
- Assign it before any IronWord call.
- Never print the key, and keep it out of every file you write or commit.
- Fetch https://ironsoftware.com/word/csharp/llms.txt first and code against what it documents.
- Look up any other member in the API reference.
- It lives at https://ironsoftware.com/word/csharp/object-reference/api/.
- Confirm that member in a build before you rely on it.
- Where the tool supports skill files, load https://ironsoftware.com/word/csharp/skill.md.
- Believe the compiler over the skill when they disagree.
- After any change that saves a DOCX, reopen that file with IronWord.
- Compare each paragraph and table cell with the text you meant to write.

Tell me which file you appended them to.
Text
Tips: Afterward, open the Step 4 project file and confirm that it holds the rules.

Step 6: Prompt Patterns That Produce Working Code

Specify the paragraphs, styling, and table.

Vague Make a Word report with IronWord.

Better Add the title as its own paragraph whose run has a TextStyle of bold 20 point Calibri, add each body paragraph with AddParagraph, then add a two-column table with one row per metric.

State the IronWord release and target framework, since members change between releases.

Better Use IronWord 2026.10.4 on .NET 9, and set the license key from IRONWORD_LICENSE_KEY before the first save.

Ask for whole files. A snippet can leave out the IronWord.Models directive that paragraph, run, and table types require, or the line that sets the key.

Better Send back the full Program.cs, using directives and license assignment included, so it builds unchanged.


Step 7: Verify What You Get Back

Each mistake below is reproducible in 2026.10.4.

  • Styling the text element: the size, color, weight, and font name on TextContent only report the effective style, and the size and font name on a Run are also read-only, so assigning them fails with CS0200. Apply a TextStyle to the run instead.
  • Building paragraphs with the document's own add-text call: WordDocument.AddText appends to the last paragraph instead of starting a new one.
  • Treating the shipped XML as the API: the skill file tells agents to search IronWord.xml for member names, but in 2026.10.4 that file documents non-public members, such as a replace-across-runs method on paragraphs and a Markdown export on the document, and omits the page-scoped text extraction, which does compile. Calls to the hidden members fail with CS1061.
  • Saving one document twice: a second save of a document built in memory writes its whole body again after the first copy, so a notice with three paragraphs and a table is saved with six paragraphs and two tables. Build a new document for each file you save.
  • Editing a loaded document that contains a table: when a DOCX with a table is loaded and saved, a replacement inside the table or in a later paragraph is silently lost, although the document in memory shows it. Documents built in memory, and loaded documents without a table, retain their edits.

Let the Agent Verify Its Own Work

An agent can run the same checks and report each result.

Rebuild in Release and run it.
If it's a web app, start it in the background.
Call each endpoint that saves or returns a DOCX, then stop the app.

After that, show me the evidence:
- Name each IronWord type and member the code calls, with the docs page where it appears.
- Report the value of IronWord.License.IsLicensed.
- Reopen each DOCX the program produced.
- Count the paragraphs outside tables and the table rows in each file.

For every paragraph and cell in each file, list the text written and the text found.
Say whether the two agree.
Text

Text extraction with IronWord

In 2026.10.4, the extraction calls return different results.

  • The document's text extraction joins the body paragraphs with no separator and returns no table text.
  • Its paragraph list holds the body paragraphs and every table cell paragraph, in document order.
  • Table rows hold cells typed as an interface, so cast each cell to TableCell to read its text.
  • A cell's own text extraction joins its paragraphs with no separator.

For a report with a title, one body paragraph, and a four-row table, the paragraph list returns 10 entries, 8 of which are table cells. A cell paragraph has a TableCell parent, so a reader can separate body text from table text. The text extraction guide shows how to read a cell.


Placeholder replacement with IronWord

A loaded notice held CUSTOMER_NAME in two body paragraphs and one table cell, with the first body copy split across two runs. IronWord 2026.10.4 replaced it in memory three ways.

Replace callBody paragraphsTable cell
ReplaceText on the documentReplaced, including the copy split across runsUnchanged
ReplaceText on each entry of ParagraphsReplacedReplaced
ReplaceText on each table cellUnchangedReplaced

The split copy is replaced in a loaded file because IronWord merges adjacent plain-text runs on load. A paragraph built in memory from two runs was also replaced across the split. For MERGEFIELD templates, the mail merge guide covers the separate merge API.


A Result You Can Reproduce

The whole test is the prompt below, and the assistant writes all of the code. Open an empty folder, start a new session there, and paste the prompt. Then compare the result with the criteria under What passes.

Please note: Before starting, install the .NET 9 SDK and set an IronWord trial or production key in IRONWORD_LICENSE_KEY. The assistant needs network access and permission to run terminal commands.

The prompt

Build a .NET 9 minimal API that writes internal report documents as DOCX and reads them back.
Start in an empty directory. If it holds a project file or code, say so and stop.
No one can answer questions, so plan, code, build, run, and repair on your own.
Settle open points sensibly and write each choice down.
Keep every deliverable in the project directory.

Do these steps in order:
1. Install IronWord 2026.10.4 from NuGet as the only DOCX library, and write every DOCX with it.
2. Use only members in https://ironsoftware.com/word/csharp/llms.txt, its guides, or API reference.
3. At startup, assign IRONWORD_LICENSE_KEY to IronWord.License.LicenseKey.

The key follows these rules:
- Never view, print, echo, hard-code, or save it, or list the environment.
- If IronWord.License.IsLicensed is false, report that and stop.

4. Add POST /documents and POST /extract.

POST /documents takes JSON and returns a DOCX built like this:
- The title is one paragraph, bold at 20 point.
- Each body paragraph is its own paragraph.
- Optional label and value rows form a two-column table.
- An absent or empty row list gives no table.

POST /extract takes one DOCX upload and returns JSON with two fields:
- "paragraphs" lists paragraphs outside tables that hold text, in document order.
- "rows" holds one array of cell texts per table row.

5. In process, write 8 DOCX files to testset/ with the /documents code and title style.

The set holds these files:
- letter-01 to letter-03: a title and three body paragraphs.
- report-01 to report-03: a title, one body paragraph, and five label and value rows.
- notice-01 and notice-02: a title, two body paragraphs, and a two-row table.
- After replacement, no title, body paragraph, or table row repeats in the set.
- manifest.json gives each file's title, customer name, and replaced paragraphs and rows.

Build each notice this way:
- Its rows are Reference and an ID you pick, then Customer and CUSTOMER_NAME.
- Body paragraph one is "Dear CUSTOMER_NAME," as the runs "Dear CUSTOMER_" and "NAME,".
- Body paragraph two mentions CUSTOMER_NAME again.
- Extend the /documents code so it takes runs.
- Save the notice as built to pre-replacement/notice-01.docx or pre-replacement/notice-02.docx.
- Replace each CUSTOMER_NAME with its customer name through IronWord's ReplaceText.
- Only then save it to testset/.

6. Before /extract is first called, check each testset file's word/document.xml yourself.

Fix the generator whenever a check fails.
Once /extract code reads a testset file, over HTTP or in process, the testset is final.
Open it as a ZIP with only System.IO.Compression and System.Xml.Linq, and pass these checks:
- The first paragraph's run is bold at 40 half-points.
- Paragraphs outside tables that hold text match the manifest in number and text.
- So do table rows.
- CUSTOMER_NAME and CUSTOMER_ occur nowhere in it.
- Each pre-replacement notice keeps body paragraph one in exactly those two runs.

7. Make /extract read every upload with IronWord, the same way each time.

The /extract code has these limits:
- It uses no other DOCX reader, no ZIP or XML parsing, and not the manifest.
- It holds no title, paragraph, label, value, or customer name as a literal.
- It never picks a reading by comparing it with expected text.
- You may change how it reads between runs. The program may not.

8. Build in Release and launch the API.

Then make these requests in order:
- Send /documents your own title, four rows, and two body paragraphs, none from the testset.
- Save the returned DOCX as report.docx.
- POST report.docx and every testset file to /extract over HTTP.
- Save each raw response body as extract/<file name without .docx>.json.

9. Write roundtrip.md in three parts.

Part one gives each file's step 6 values, with report.docx measured against your request.
Give title size in half-points and placeholders as CUSTOMER_NAME or CUSTOMER_ occurrences.
Part two describes the /extract method and the ReplaceText calls that made the replacements.
Part three is a 50-row table, one row per expected title, body paragraph, and table row.
Its columns are File, Element, Expected text, Text read, and Match, filled this way:
- Element is title, paragraph N, or row N.
- Expected text comes from the manifest or the request.
- Text read is copied from the saved response, wrong or not, or is (missing) if absent.
- A row's text, expected or read, is its label and value joined by ": ".
- So no label, value, ID, or name contains a colon.
- Match needs exactly equal text at the same position.
- An extra or missing paragraph, row, or cell fails all of that file's rows.

10. Until all 50 rows match, study the failing responses and change the /extract method.

Each pass follows these rules:
- Rebuild and rerun after each change.
- Keep only the latest responses in extract/.
- Stop after the sixth run that still fails.

11. Finish with a short summary that gives me these things:
- The Release build result and rows matched out of 50.
- The /extract method, the step 6 values, and each assumption you relied on.
- If the sixth run fails, the failing rows, each method tried, and what you think causes them.
Text

Output

The run's unedited round-trip table lists the measured values for all 11 files and the 50 rows. report.docx is the document that POST /documents returned.

  • Each pre-replacement notice contained 3 placeholders, with the greeting split into the runs "Dear CUSTOMER_" and "NAME,".
  • Each saved notice contained no placeholders, and its greeting stayed two runs with the comma in the second.
  • All 11 files passed the step 6 checks, with every title bold at 40 half-points.
  • All 50 rows matched on the first call to the extract endpoint, so the step 10 repair loop never ran.
  • Claude Code found the font size and second save behaviors in scratch documents and built each notice twice.
  • The extract endpoint dropped table cell paragraphs from the paragraph list and read the cells row by row.
  • The documentation prompt changed no code and found TableCell.Paragraphs only in the API reference.
  • Step 5 wrote its rules and the measured behaviors to a new CLAUDE.md.
  • The self-check matched all 87 paragraphs and cells across the 11 files.
  • Our own checks on the saved files passed every requirement.
  • A fresh start of the Release build returned the same 9 responses.
  • Some steps ran differently from the page. The prompts for Steps 1, 3, 5, and 7 came after the acceptance prompt, in that same session. During the acceptance prompt, the API that Claude Code had started blocked a rebuild, so Claude Code restarted it and matched 50 of 50 again.

What passes

  • The project builds in Release with no change made by hand.
  • The step 6 values and the 50 matching rows are reproduced when you measure the saved files yourself.
  • The license key appears in no file of the project folder.
Please note: To try another release, set its version number in step 1 and read its entry in the IronWord changelog.

The run record

  • One Windows 11 x64 desktop, with a Core i9-14900KF processor and 64 GB of memory, ran every test.
  • Each project targets .NET 9.
  • The runs used IronWord 2026.10.1. The prompts were then pinned to 2026.10.4, published on October 12, 2026, which adds a .NET 8 build and resolves CVE-2026-106110 through CVE-2026-106116 by moving SixLabors.ImageSharp to 4.1.3 on .NET 8 and later.
  • 2026.10.4 exposes the same public API and the same XML documentation as 2026.10.1, and 32 probe runs on 2026.10.4 under .NET 9 gave the same results as on 2026.10.1.
  • The test projects in the five tool guides run on xUnit, and Antigravity resized its thumbnails with SixLabors.ImageSharp, which IronWord references through IronSoftware.System.Drawing.
  • Duration adds up the logged minutes of every prompt on a guide's page. This guide's row counts the acceptance prompt alone.
  • Last tested October 15, 2026, for this guide, GitHub Copilot, Claude Code, Google Antigravity and ChatGPT Codex, and October 12, 2026, for Cursor.
GuideAssistant and versionModelDuration (minutes)
This guideClaude Code 2.1.277, one session, permission prompts offClaude Opus 516.2
Claude CodeClaude Code 2.1.277, opusplan alias, prompt 3 in plan modeClaude Sonnet 5, with Claude Opus 5 planning prompt 335.4
ChatGPT CodexCodex CLI 0.160.0, one sittingGPT-6 Luna43.2
GitHub CopilotCopilot CLI 1.0.97, prompt 3 in plan mode, the review as its own callGPT-6 Luna, picked by Auto12.8
CursorCursor 3.23.23 desktop app, prompt 3 in Plan ModeGrok 4.723.0
Google AntigravityAntigravity CLI 1.3.5, permission prompts skippedGemini 3.8 Flash (High)43.2

Agent-Specific Guides

Each guide below runs these steps with one tool on one Word document task.


Troubleshooting


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.

Read More

Ready to Get Started?

Nuget Downloads 58,016Version:2026.10just released