Powerpoint Anyone?

The Question

“I am a development manager. I need to have a 1 hour training session to show my group the business case and technical details for a RemittanceAdvice message. It should introduce them to the message as if they’ve never heard of it. It should show technical details about the implementation of the message, validation, and the hierarchy. Can you create a PowerPoint presentation to help train them?”

Not a wiki page. Not a README. A PowerPoint deck — ready to present, with speaker notes, structured for a one-hour session with a development team that has never touched ISO 20022.


Step 1 — The MCP Server Reads the Full Spec

The AI queries the Beneficial Strategies ISO 20022 MCP Server and retrieves everything about remt.001.001.06 — RemittanceAdviceV06:

No PDF opened. No spec site navigated manually.


Step 2 — Two Clarifying Questions

“1. Should the deck assume the audience knows XML and C#, but not ISO 20022 specifically?”

“2. Should it include a live code walkthrough slide, or keep implementation at the conceptual level?”

Answers: Yes — developers who know XML and C# but are ISO 20022 newcomers. And yes, include a code walkthrough: this is a developer training, not an executive briefing.


Step 3 — Everything Is Generated Automatically

Presentation Structure (12 Slides, ~5 min each)

Slide Title Purpose
1 What Is a Remittance? Business hook — the problem it solves
2 Where RemittanceAdvice Fits ISO 20022 business area map
3 The Business Case Why your A/R team cares
4 Message Identity remt.001.001.06, namespace, XSD
5 Message Hierarchy Building block diagram
6 GroupHeader Deep Dive Required fields, party roles
7 RemittanceInformation — Unstructured When and why to use free text
8 RemittanceInformation — Structured Invoice references, amounts, tax, garnishment
9 Validation Rules Schema vs. business rules
10 C# Implementation Data containers and serialization
11 Common Mistakes Real gotchas from the spec
12 Q&A / Next Steps What to build next

Message Hierarchy (Slide 5 Diagram)

classDiagram
    direction TB
    class RemittanceAdviceV06{
        MessageId remt001001006
    }
    RemittanceAdviceV06 *-- "1..1" GroupHeader122 : GroupHeader
    RemittanceAdviceV06 *-- "1..*" RemittanceInformation23 : RemittanceInformation
    RemittanceAdviceV06 *-- "0..*" SupplementaryData1 : SupplementaryData

    class GroupHeader122{
        MessageIdentification Max35Text
        CreationDateTime ISODateTime
    }
    GroupHeader122 *-- "1..1" PartyIdentification272 : InitiatingParty
    GroupHeader122 *-- "0..1" PartyIdentification272 : MessageRecipient

    class RemittanceInformation23{
        RemittanceIdentification Max35Text
        Unstructured Max140Text
    }
    RemittanceInformation23 *-- "0..*" StructuredRemittanceInformation18 : Structured
    RemittanceInformation23 *-- "1..1" OriginalPaymentInformation10 : OriginalPaymentInformation

    class StructuredRemittanceInformation18{
        AdditionalRemittanceInformation Max140Text
    }
    StructuredRemittanceInformation18 *-- "0..*" ReferredDocumentInformation8 : ReferredDocumentInformation
    StructuredRemittanceInformation18 *-- "0..1" RemittanceAmount4 : ReferredDocumentAmount
    StructuredRemittanceInformation18 *-- "0..1" CreditorReferenceInformation3 : CreditorReferenceInformation
    StructuredRemittanceInformation18 *-- "0..1" PartyIdentification272 : Invoicer
    StructuredRemittanceInformation18 *-- "0..1" PartyIdentification272 : Invoicee
    StructuredRemittanceInformation18 *-- "0..1" TaxData1 : TaxRemittance
    StructuredRemittanceInformation18 *-- "0..1" Garnishment4 : GarnishmentRemittance
  

Sample XML (Slide 8 — Structured Remittance)

A complete remt.001.001.06 message paying two invoices with one payment:

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:remt.001.001.06">
  <RmtAdvc>

    <!-- Slide 6: GroupHeader — who sent it and when -->
    <GrpHdr>
      <MsgId>REMT-2026-00099</MsgId>
      <CreDtTm>2026-03-17T09:00:00</CreDtTm>
      <InitgPty>
        <Nm>Beneficial Strategies LLC</Nm>
        <Id><OrgId><AnyBIC>BSTRUSXX</AnyBIC></OrgId></Id>
      </InitgPty>
      <MsgRcpt>
        <Nm>Acme Corp</Nm>
        <Id><OrgId><AnyBIC>ACMEUSNY</AnyBIC></OrgId></Id>
      </MsgRcpt>
    </GrpHdr>

    <!-- Slide 8: RemittanceInformation — what this payment covers -->
    <RmtInf>
      <RmtId>RMTINF-001</RmtId>

      <!-- Unstructured: human-readable note -->
      <Ustrd>Payment for invoices INV-2026-042 and INV-2026-043</Ustrd>

      <!-- Structured: machine-readable invoice references -->
      <Strd>
        <RfrdDocInf>
          <Tp><CdOrPrtry><Cd>CINV</Cd></CdOrPrtry></Tp>
          <Nb>INV-2026-042</Nb>
          <RltdDt><Dt>2026-03-01</Dt></RltdDt>
        </RfrdDocInf>
        <RfrdDocAmt>
          <RmtAmtAndTp>
            <Tp><CdOrPrtry><Cd>CINV</Cd></CdOrPrtry></Tp>
            <Amt Ccy="USD">2500.00</Amt>
          </RmtAmtAndTp>
        </RfrdDocAmt>
        <Invcr>
          <Nm>Beneficial Strategies LLC</Nm>
        </Invcr>
        <Invcee>
          <Nm>Acme Corp</Nm>
        </Invcee>
        <AddtlRmtInf>Consulting services Q1 2026</AddtlRmtInf>
      </Strd>

      <Strd>
        <RfrdDocInf>
          <Tp><CdOrPrtry><Cd>CINV</Cd></CdOrPrtry></Tp>
          <Nb>INV-2026-043</Nb>
          <RltdDt><Dt>2026-03-10</Dt></RltdDt>
        </RfrdDocInf>
        <RfrdDocAmt>
          <RmtAmtAndTp>
            <Tp><CdOrPrtry><Cd>CINV</Cd></CdOrPrtry></Tp>
            <Amt Ccy="USD">2250.00</Amt>
          </RmtAmtAndTp>
        </RfrdDocAmt>
        <AddtlRmtInf>Software license renewal</AddtlRmtInf>
      </Strd>

      <!-- Links back to the original payment instruction -->
      <OrgnlPmtInf>
        <OrgnlMsgId>PAIN001-2026-00077</OrgnlMsgId>
        <OrgnlMsgNmId>pain.001.001.12</OrgnlMsgNmId>
        <OrgnlCreDtTm>2026-03-17T08:55:00</OrgnlCreDtTm>
      </OrgnlPmtInf>
    </RmtInf>

  </RmtAdvc>
</Document>

C# Implementation (Slide 10)

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Iso20022.Remt.V06
{
    // Root document — namespace ties it to remt.001.001.06
    [XmlRoot("Document", Namespace = "urn:iso:std:iso:20022:tech:xsd:remt.001.001.06")]
    public class Document
    {
        [XmlElement("RmtAdvc")]
        public RemittanceAdviceV06 RemittanceAdvice { get; set; }
    }

    // Top-level message
    public class RemittanceAdviceV06
    {
        [XmlElement("GrpHdr")]
        public GroupHeader122 GroupHeader { get; set; }

        [XmlElement("RmtInf")]
        public List<RemittanceInformation23> RemittanceInformation { get; set; } = new();
    }

    // GroupHeader: who sent it, when, routing
    public class GroupHeader122
    {
        [XmlElement("MsgId")]
        public string MessageIdentification { get; set; }   // Required, Max35Text

        [XmlElement("CreDtTm")]
        public DateTime CreationDateTime { get; set; }       // Required, ISODateTime

        [XmlElement("InitgPty")]
        public PartyIdentification272 InitiatingParty { get; set; } // Required

        [XmlElement("MsgRcpt")]
        public PartyIdentification272 MessageRecipient { get; set; } // Optional
    }

    // RemittanceInformation: one block per payment being explained
    public class RemittanceInformation23
    {
        [XmlElement("RmtId")]
        public string RemittanceIdentification { get; set; }  // Optional, Max35Text

        // Free-text description — quick and simple, but not machine-readable
        [XmlElement("Ustrd")]
        public List<string> Unstructured { get; set; } = new();

        // Structured invoice references — preferred for A/R automation
        [XmlElement("Strd")]
        public List<StructuredRemittanceInformation18> Structured { get; set; } = new();

        [XmlElement("OrgnlPmtInf")]
        public OriginalPaymentInformation10 OriginalPaymentInformation { get; set; } // Required
    }

    // Structured remittance: links payment to specific invoices
    public class StructuredRemittanceInformation18
    {
        [XmlElement("RfrdDocInf")]
        public List<ReferredDocumentInformation8> ReferredDocumentInformation { get; set; } = new();

        [XmlElement("RfrdDocAmt")]
        public RemittanceAmount4 ReferredDocumentAmount { get; set; }

        [XmlElement("CdtrRefInf")]
        public CreditorReferenceInformation3 CreditorReferenceInformation { get; set; }

        [XmlElement("Invcr")]
        public PartyIdentification272 Invoicer { get; set; }  // Who issued the invoice

        [XmlElement("Invcee")]
        public PartyIdentification272 Invoicee { get; set; }  // Who received the invoice

        [XmlElement("TaxRmt")]
        public TaxData1 TaxRemittance { get; set; }

        [XmlElement("GrnshmtRmt")]
        public Garnishment4 GarnishmentRemittance { get; set; }

        // Up to 3 additional free-text notes (Max 3 per spec)
        [XmlElement("AddtlRmtInf")]
        public List<string> AdditionalRemittanceInformation { get; set; } = new();
    }

    // ReferredDocumentInformation: identifies the invoice being paid
    public class ReferredDocumentInformation8
    {
        [XmlElement("Tp")]
        public ReferredDocumentType4 Type { get; set; }  // e.g. CINV = Commercial Invoice

        [XmlElement("Nb")]
        public string Number { get; set; }               // Invoice number, Max35Text

        [XmlElement("RltdDt")]
        public DateAndType1 RelatedDate { get; set; }    // Invoice date
    }

    // RemittanceAmount4: amounts being paid for this document
    public class RemittanceAmount4
    {
        [XmlElement("RmtAmtAndTp")]
        public List<DocumentAmount1> RemittanceAmountAndType { get; set; } = new();

        [XmlElement("AdjstmntAmtAndRsn")]
        public List<DocumentAdjustment1> AdjustmentAmountAndReason { get; set; } = new();
    }

    // Supporting types
    public class PartyIdentification272
    {
        [XmlElement("Nm")]
        public string Name { get; set; }

        [XmlElement("Id")]
        public Party38Choice Id { get; set; }
    }

    public class OriginalPaymentInformation10
    {
        [XmlElement("OrgnlMsgId")]
        public string OriginalMessageIdentification { get; set; }

        [XmlElement("OrgnlMsgNmId")]
        public string OriginalMessageNameIdentification { get; set; }

        [XmlElement("OrgnlCreDtTm")]
        public DateTime? OriginalCreationDateTime { get; set; }
    }
}

C# PowerPoint Generation (Slide 10 continued)

The deck itself is generated programmatically using the OpenXML SDK — no PowerPoint installed required:

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using A = DocumentFormat.OpenXml.Drawing;

namespace Iso20022.Training
{
    public class RemittanceTrainingDeckGenerator
    {
        private static readonly string[] SlideData = new[]
        {
            ("What Is a Remittance?",
             "A remittance is the structured explanation of WHY a payment is being made.\n\n" +
             "Without it: payer sends $4,750. Payee has 12 open invoices. Which ones are paid?\n\n" +
             "With remt.001: payer says exactly which invoices are covered, the amounts, and the references.\n\n" +
             "Speaker note: Ask the room — has anyone had to manually match a payment to invoices? That's the problem this solves."),

            ("Where RemittanceAdvice Fits",
             "ISO 20022 Business Area: REMT (Remittance)\n\n" +
             "Message ID: remt.001.001.06\n" +
             "Namespace: urn:iso:std:iso:20022:tech:xsd:remt.001.001.06\n\n" +
             "Sent separately from the payment itself (pain.001) or alongside it.\n" +
             "The payment moves money. The remittance explains it.\n\n" +
             "Speaker note: Show the XSD at iso20022.org — this is the contract both sides implement."),

            ("The Business Case",
             "Accounts Receivable teams spend hours matching payments to invoices manually.\n\n" +
             "remt.001 makes this automatic:\n" +
             "  • Machine-readable invoice references\n" +
             "  • Exact amounts per invoice\n" +
             "  • Tax and adjustment details\n" +
             "  • Creditor reference for cross-system lookup\n\n" +
             "ROI: reduces payment-to-close time from days to minutes.\n\n" +
             "Speaker note: If your client runs SAP or Oracle Financials, they already expect this format."),

            ("Message Identity",
             "Full message ID: remt.001.001.06\n" +
             "  remt  = business area (Remittance)\n" +
             "  001   = message functionality\n" +
             "  001   = flavour\n" +
             "  06    = version 6\n\n" +
             "XSD schema: https://www.iso20022.org/sites/default/files/documents/messages/remt/schemas/remt.001.001.06.xsd\n\n" +
             "Always validate your output against this schema before transmission.\n\n" +
             "Speaker note: Version 06 is current. Never build against v01 — the spec has evolved significantly."),

            ("Message Hierarchy",
             "RemittanceAdviceV06\n" +
             "  ├── GroupHeader (1..1)           — message envelope, sender/receiver\n" +
             "  │     ├── MessageIdentification   — unique message ID (required)\n" +
             "  │     ├── CreationDateTime        — timestamp (required)\n" +
             "  │     ├── InitiatingParty         — who sent it (required)\n" +
             "  │     └── MessageRecipient        — who receives it (optional)\n" +
             "  ├── RemittanceInformation (1..*) — one block per payment explained\n" +
             "  │     ├── Unstructured            — free text (0..*)\n" +
             "  │     ├── Structured              — invoice references (0..*)\n" +
             "  │     └── OriginalPaymentInfo     — links back to pain.001 (required)\n" +
             "  └── SupplementaryData (0.*)      — extensibility hook\n\n" +
             "Speaker note: The 1..* on RemittanceInformation means one message can explain multiple payments at once."),

            ("GroupHeader Deep Dive",
             "Required fields — your message is rejected without these:\n\n" +
             "  MsgId       Max35Text    Point-to-point unique reference\n" +
             "  CreDtTm     ISODateTime  Format: 2026-03-17T09:00:00\n" +
             "  InitgPty    Party        Sender identity (Name + BIC or IBAN)\n\n" +
             "Optional but important:\n" +
             "  MsgRcpt     Party        Intended recipient\n" +
             "  FwdgAgt     Institution  If routing through an intermediary\n" +
             "  CpyInd      Code         CODU/DUPL/COPY — mark duplicates!\n\n" +
             "Speaker note: MsgId must be unique per sender. Use a UUID or timestamp-based ID. Duplicates cause reconciliation chaos."),

            ("Unstructured Remittance",
             "Use <Ustrd> when:\n" +
             "  • Quick integration, human-readable only\n" +
             "  • Legacy receiver systems that parse free text\n" +
             "  • Supplementary notes alongside structured data\n\n" +
             "Example:\n" +
             "  <Ustrd>Payment for INV-2026-042 and INV-2026-043</Ustrd>\n\n" +
             "Limitations:\n" +
             "  • Not machine-readable — A/R still has to parse it manually\n" +
             "  • Max 140 characters\n" +
             "  • Cannot reference amounts, dates, or adjustment reasons\n\n" +
             "Speaker note: Think of Unstructured like the memo field on a cheque. Fine for humans; useless for automation."),

            ("Structured Remittance",
             "Use <Strd> for full A/R automation. Key elements:\n\n" +
             "  RfrdDocInf   Invoice number, type code, invoice date\n" +
             "  RfrdDocAmt   Amount paid, type (CINV/CREN/DUPA/REMI), adjustments\n" +
             "  CdtrRefInf   Creditor's own reference number\n" +
             "  Invcr        Who issued the invoice\n" +
             "  Invcee       Who received the invoice\n" +
             "  TaxRmt       Tax remittance details\n" +
             "  GrnshmtRmt   Garnishment details (payroll scenarios)\n" +
             "  AddtlRmtInf  Up to 3 free-text notes (Max140Text each)\n\n" +
             "Constraint: If Type/Code is CREN, DUPA, or REMI — RmtAmtAndTp must not repeat.\n\n" +
             "Speaker note: The constraint on CREN/DUPA/REMI is enforced by the XSD. Your serializer must know this or schema validation fails."),

            ("Validation Rules",
             "Two layers — both must pass:\n\n" +
             "LAYER 1 — Schema Validation (XSD)\n" +
             "  • Required fields present\n" +
             "  • Types correct (date as YYYY-MM-DD, not 'March 17')\n" +
             "  • Max lengths respected (Max35Text = 35 chars)\n" +
             "  • Enum values valid (CINV, CREN, DUPA, REMI only)\n" +
             "  • Namespace exact match\n\n" +
             "LAYER 2 — Business Rules\n" +
             "  • MsgId unique per sender\n" +
             "  • CREN/DUPA/REMI: RmtAmtAndTp must not repeat\n" +
             "  • AddtlRmtInf: maximum 3 occurrences\n" +
             "  • OriginalPaymentInformation must reference a real prior message\n\n" +
             "Speaker note: Schema failures are caught before processing. Business rule failures may only surface at the receiver's A/R system — often days later."),

            ("C# Implementation",
             "Key points for your implementation:\n\n" +
             "1. Use [XmlRoot] with the exact namespace on Document class\n" +
             "   Namespace = \"urn:iso:std:iso:20022:tech:xsd:remt.001.001.06\"\n\n" +
             "2. List<string> for Unstructured — it's 0..* in the spec\n\n" +
             "3. List<string> for AddtlRmtInf — but enforce max 3 in your builder\n\n" +
             "4. Always serialize then validate against XSD before sending:\n" +
             "   var schemas = new XmlSchemaSet();\n" +
             "   schemas.Add(null, \"remt.001.001.06.xsd\");\n" +
             "   // ValidationEventHandler catches schema errors\n\n" +
             "5. OriginalPaymentInformation is REQUIRED (1..1) — cannot be omitted\n\n" +
             "Speaker note: Step 4 is non-negotiable. Transmitting an invalid message wastes network round-trips and may trigger sanctions from the receiving bank."),

            ("Common Mistakes",
             "Mistake 1: Using Unstructured when Structured is available\n" +
             "  → Your A/R team ends up parsing free text. Defeats the purpose.\n\n" +
             "Mistake 2: Omitting OriginalPaymentInformation\n" +
             "  → Required (1..1). Missing it = schema failure = rejected.\n\n" +
             "Mistake 3: Wrong namespace\n" +
             "  → Using remt.001.001.01 namespace for a v06 message = rejected.\n\n" +
             "Mistake 4: More than 3 AddtlRmtInf elements\n" +
             "  → Schema allows 0..3. A 4th element fails validation silently in some parsers.\n\n" +
             "Mistake 5: Repeating RmtAmtAndTp with CREN/DUPA/REMI type code\n" +
             "  → Explicit spec constraint. XSD will reject it.\n\n" +
             "Speaker note: Print this slide. These are the five bugs that will consume the first two sprints if not addressed up front."),

            ("Next Steps",
             "1. Download the XSD:\n" +
             "   iso20022.org → remt.001.001.06.xsd\n\n" +
             "2. Scaffold the C# data containers\n" +
             "   (available in the Beneficial Strategies ISO 20022 library)\n\n" +
             "3. Build a schema-validating serializer harness\n\n" +
             "4. Write test cases for:\n" +
             "   • Happy path (structured, two invoices)\n" +
             "   • Unstructured only\n" +
             "   • CREN type code (no repeat constraint)\n" +
             "   • Missing OriginalPaymentInformation (expect rejection)\n\n" +
             "5. Ask the AI: 'Generate a full QA test plan for remt.001'\n\n" +
             "Speaker note: Step 5 is not a joke. The MCP server will generate 50+ test cases from the spec automatically.")
        };

        public static void Generate(string outputPath)
        {
            using var presentation = PresentationDocument.Create(
                outputPath, PresentationDocumentType.Presentation);

            var presentationPart = presentation.AddPresentationPart();
            presentationPart.Presentation = new Presentation();

            var slideIdList = new SlideIdList();
            uint slideId = 256;

            foreach (var (title, body) in SlideData)
            {
                var slidePart = presentationPart.AddNewPart<SlidePart>();
                BuildSlide(slidePart, title, body);

                slideIdList.Append(new SlideId
                {
                    Id = slideId++,
                    RelationshipId = presentationPart.GetIdOfPart(slidePart)
                });
            }

            presentationPart.Presentation.Append(slideIdList);
            presentationPart.Presentation.Save();
        }

        private static void BuildSlide(SlidePart slidePart, string title, string body)
        {
            // Parse speaker note out of body (last paragraph starting with "Speaker note:")
            var speakerNote = string.Empty;
            var bodyLines = body.Split('\n').ToList();
            var noteIdx = bodyLines.FindIndex(l => l.StartsWith("Speaker note:"));
            if (noteIdx >= 0)
            {
                speakerNote = bodyLines[noteIdx]["Speaker note:".Length..].Trim();
                bodyLines = bodyLines.Take(noteIdx).ToList();
                body = string.Join('\n', bodyLines).TrimEnd();
            }

            slidePart.Slide = new Slide(
                new CommonSlideData(
                    new ShapeTree(
                        new NonVisualGroupShapeProperties(
                            new NonVisualDrawingProperties { Id = 1, Name = "" },
                            new NonVisualGroupShapeDrawingProperties(),
                            new ApplicationNonVisualDrawingProperties()),
                        new GroupShapeProperties(new A.TransformGroup()),
                        BuildTitleShape(title),
                        BuildBodyShape(body))));

            if (!string.IsNullOrEmpty(speakerNote))
                AddSpeakerNotes(slidePart, speakerNote);

            slidePart.Slide.Save();
        }

        private static Shape BuildTitleShape(string title) => new(
            new NonVisualShapeProperties(
                new NonVisualDrawingProperties { Id = 2, Name = "Title" },
                new NonVisualShapeDrawingProperties(new A.ShapeLocks { NoGrouping = true }),
                new ApplicationNonVisualDrawingProperties(
                    new PlaceholderShape { Type = PlaceholderValues.Title })),
            new ShapeProperties(),
            new TextBody(
                new A.BodyProperties(),
                new A.ListStyle(),
                new A.Paragraph(new A.Run(
                    new A.RunProperties { Language = "en-US", Bold = true },
                    new A.Text(title)))));

        private static Shape BuildBodyShape(string body) => new(
            new NonVisualShapeProperties(
                new NonVisualDrawingProperties { Id = 3, Name = "Body" },
                new NonVisualShapeDrawingProperties(new A.ShapeLocks { NoGrouping = true }),
                new ApplicationNonVisualDrawingProperties(
                    new PlaceholderShape { Index = 1 })),
            new ShapeProperties(),
            new TextBody(
                new A.BodyProperties(),
                new A.ListStyle(),
                body.Split('\n').Select(line =>
                    new A.Paragraph(new A.Run(
                        new A.RunProperties { Language = "en-US", FontSize = 1400 },
                        new A.Text(line))))
                    .ToArray<OpenXmlElement>()));

        private static void AddSpeakerNotes(SlidePart slidePart, string note)
        {
            var notesPart = slidePart.AddNewPart<NotesSlidePart>();
            notesPart.NotesSlide = new NotesSlide(
                new CommonSlideData(
                    new ShapeTree(
                        new NonVisualGroupShapeProperties(
                            new NonVisualDrawingProperties { Id = 1, Name = "" },
                            new NonVisualGroupShapeDrawingProperties(),
                            new ApplicationNonVisualDrawingProperties()),
                        new GroupShapeProperties(new A.TransformGroup()),
                        new Shape(
                            new NonVisualShapeProperties(
                                new NonVisualDrawingProperties { Id = 2, Name = "Notes" },
                                new NonVisualShapeDrawingProperties(),
                                new ApplicationNonVisualDrawingProperties(
                                    new PlaceholderShape { Type = PlaceholderValues.Body })),
                            new ShapeProperties(),
                            new TextBody(
                                new A.BodyProperties(),
                                new A.ListStyle(),
                                new A.Paragraph(new A.Run(new A.Text(note))))))));
            notesPart.NotesSlide.Save();
        }
    }
}

Usage

// Generate the deck — no PowerPoint installation required
RemittanceTrainingDeckGenerator.Generate("RemittanceAdvice-Training.pptx");
Console.WriteLine("Deck saved. Open in PowerPoint or Google Slides.");

Add the NuGet package to your project:

<PackageReference Include="DocumentFormat.OpenXml" Version="3.1.0" />

What Just Happened?

A development manager asked for a one-hour training deck. The Beneficial Strategies ISO 20022 MCP Server read the full remt.001.001.06 specification — every building block, every field constraint, every multiplicity rule — and the AI turned that into:

The only decisions you made were the audience level and whether to include a code walkthrough.


“But I Don’t Write Code. Can I Just Get the File?”

Yes. If you are using Claude Code — the desktop AI assistant — you don’t need to be a developer at all. Claude Code doesn’t just write code: it runs it. You ask the question, Claude queries the MCP server, writes a Python script, executes it on your machine, and hands you a .pptx file ready to open in PowerPoint or Google Slides.

Here is exactly what that looks like.

What the Manager Says

“I don’t write code. Can you just create the PowerPoint file for me right now?”

What Claude Code Does — Automatically

Step 1: Checks for python-pptx and installs it if missing.

pip install python-pptx

Step 2: Writes this Python script and runs it immediately.

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

SLIDES = [
    (
        "What Is a Remittance?",
        (
            "A remittance is the structured explanation of WHY a payment is being made.\n\n"
            "Without it: payer sends $4,750. Payee has 12 open invoices. Which ones are paid?\n\n"
            "With remt.001: payer says exactly which invoices are covered, the amounts, "
            "and the references — in a machine-readable format both systems can process automatically."
        ),
        "Ask the room: has anyone had to manually match a payment to invoices? "
        "That's the problem this solves."
    ),
    (
        "Where RemittanceAdvice Fits",
        (
            "ISO 20022 Business Area: REMT (Remittance)\n\n"
            "Message ID:  remt.001.001.06\n"
            "Namespace:   urn:iso:std:iso:20022:tech:xsd:remt.001.001.06\n\n"
            "Sent separately from the payment (pain.001) or alongside it.\n\n"
            "The payment moves the money.\n"
            "The remittance advice explains what the money is for."
        ),
        "Show the XSD at iso20022.org — this is the contract both sides implement. "
        "Version 06 is current. Never build against v01."
    ),
    (
        "The Business Case",
        (
            "A/R teams spend hours manually matching payments to invoices.\n\n"
            "remt.001 makes this automatic:\n"
            "  \u2022  Machine-readable invoice references\n"
            "  \u2022  Exact amounts per invoice\n"
            "  \u2022  Tax and adjustment details\n"
            "  \u2022  Creditor reference for cross-system lookup\n\n"
            "ROI: reduces payment-to-close time from days to minutes.\n\n"
            "If your client runs SAP or Oracle Financials, they already expect this format."
        ),
        "Quantify this for the room: how many hours per week does your A/R team spend "
        "on manual payment matching? That's the budget justification."
    ),
    (
        "Message Identity",
        (
            "Full message ID:  remt.001.001.06\n\n"
            "  remt  \u2192  business area (Remittance)\n"
            "  001   \u2192  message functionality\n"
            "  001   \u2192  flavour\n"
            "  06    \u2192  version 6 (current)\n\n"
            "XSD Schema:\n"
            "  iso20022.org \u2192 remt \u2192 schemas \u2192 remt.001.001.06.xsd\n\n"
            "Always validate your output against this schema before transmission.\n"
            "A non-conformant message is silently rejected."
        ),
        "The XSD is the single source of truth. If your message passes XSD validation "
        "and the business rules, it will be accepted."
    ),
    (
        "Message Hierarchy",
        (
            "RemittanceAdviceV06  (remt.001.001.06)\n"
            "\u251c\u2500\u2500 GroupHeader          (1..1)   envelope: sender, receiver, timestamp\n"
            "\u2502     \u251c\u2500\u2500 MessageIdentification   required, unique per sender\n"
            "\u2502     \u251c\u2500\u2500 CreationDateTime        required, ISO datetime\n"
            "\u2502     \u251c\u2500\u2500 InitiatingParty         required, who sent it\n"
            "\u2502     \u2514\u2500\u2500 MessageRecipient        optional, who receives it\n"
            "\u251c\u2500\u2500 RemittanceInformation (1..*) one block per payment explained\n"
            "\u2502     \u251c\u2500\u2500 Unstructured            free text (0..*)\n"
            "\u2502     \u251c\u2500\u2500 Structured              invoice references (0..*)\n"
            "\u2502     \u2514\u2500\u2500 OriginalPaymentInfo     links to pain.001 (required)\n"
            "\u2514\u2500\u2500 SupplementaryData    (0.*) extensibility hook"
        ),
        "The 1..* on RemittanceInformation means one message can explain multiple payments. "
        "The 1..1 on OriginalPaymentInformation means this field is never optional — "
        "missing it causes an immediate schema rejection."
    ),
    (
        "GroupHeader: Required Fields",
        (
            "These fields are REQUIRED (1..1). Missing any one = message rejected.\n\n"
            "  MsgId      Max35Text    Unique reference, assigned by sender\n"
            "  CreDtTm    ISODateTime  Format: 2026-03-17T09:00:00\n"
            "  InitgPty   Party        Sender name + BIC or IBAN\n\n"
            "Optional but important:\n\n"
            "  MsgRcpt    Party        Intended recipient\n"
            "  FwdgAgt    Institution  Intermediary routing agent\n"
            "  CpyInd     Code         Mark copies: CODU / DUPL / COPY\n\n"
            "MsgId must be UNIQUE per sender.\n"
            "Duplicate IDs cause reconciliation failures at the receiver."
        ),
        "MsgId uniqueness is a business rule, not enforced by XSD. "
        "Your implementation must generate a unique ID — timestamp + UUID is the common pattern."
    ),
    (
        "Unstructured Remittance",
        (
            "Use <Ustrd> when:\n"
            "  \u2022  Quick integration — human-readable only\n"
            "  \u2022  Legacy receiver systems that parse free text\n"
            "  \u2022  Adding a note alongside structured data\n\n"
            "Example:\n"
            '  <Ustrd>Payment for INV-2026-042 and INV-2026-043</Ustrd>\n\n'
            "Limitations:\n"
            "  \u2022  Not machine-readable — A/R still matches manually\n"
            "  \u2022  Maximum 140 characters\n"
            "  \u2022  Cannot express amounts, dates, or adjustment reasons"
        ),
        "Think of Unstructured like the memo field on a cheque. "
        "Fine for humans, useless for automation. "
        "Always prefer Structured when the receiver supports it."
    ),
    (
        "Structured Remittance",
        (
            "Use <Strd> for full A/R automation.\n\n"
            "  RfrdDocInf    Invoice number, type code (CINV), invoice date\n"
            "  RfrdDocAmt    Amount paid, type, adjustments and reasons\n"
            "  CdtrRefInf    Creditor's own cross-reference number\n"
            "  Invcr         Who issued the invoice\n"
            "  Invcee        Who received the invoice\n"
            "  TaxRmt        Tax remittance breakdown\n"
            "  GrnshmtRmt    Garnishment details (payroll scenarios)\n"
            "  AddtlRmtInf   Up to 3 free-text notes (140 chars each)\n\n"
            "KEY CONSTRAINT:\n"
            "  If Type/Code = CREN, DUPA, or REMI \u2192\n"
            "  RmtAmtAndTp must NOT repeat. XSD will reject it."
        ),
        "The CREN/DUPA/REMI constraint is the #1 spec gotcha. "
        "Credit notes and duplicates get special treatment. "
        "Make sure your builder enforces this before serialization."
    ),
    (
        "Validation: Two Layers",
        (
            "LAYER 1 \u2014 Schema Validation (XSD)\n"
            "  Catches structural problems before processing:\n"
            "  \u2022  Required fields present\n"
            "  \u2022  Types correct (date as YYYY-MM-DD, not 'March 17')\n"
            "  \u2022  Max lengths respected (Max35Text = 35 chars max)\n"
            "  \u2022  Enum values valid (CINV, CREN, DUPA, REMI only)\n"
            "  \u2022  Namespace exact match to version\n\n"
            "LAYER 2 \u2014 Business Rules\n"
            "  Catches logic problems after parsing:\n"
            "  \u2022  MsgId unique per sender\n"
            "  \u2022  CREN/DUPA/REMI: RmtAmtAndTp must not repeat\n"
            "  \u2022  AddtlRmtInf: maximum 3 occurrences\n"
            "  \u2022  OriginalPaymentInformation must reference real message"
        ),
        "Schema failures are caught immediately. "
        "Business rule failures may surface days later at the receiver's A/R system. "
        "Both layers must pass."
    ),
    (
        "Implementation: Key Rules",
        (
            "1.  Namespace on Document class must be exact:\n"
            "      urn:iso:std:iso:20022:tech:xsd:remt.001.001.06\n\n"
            "2.  Unstructured is a list \u2014 the spec allows 0..*\n\n"
            "3.  AddtlRmtInf is a list \u2014 but enforce max 3 in your builder\n\n"
            "4.  ALWAYS validate before sending:\n"
            "      schemas = XmlSchemaSet()\n"
            "      schemas.Add(None, 'remt.001.001.06.xsd')\n"
            "      # ValidationEventHandler catches schema errors\n\n"
            "5.  OriginalPaymentInformation is REQUIRED (1..1)\n"
            "    It cannot be omitted. Ever."
        ),
        "Rule 4 is non-negotiable. Transmitting an invalid message wastes network "
        "round-trips and may trigger compliance flags at the receiving bank."
    ),
    (
        "Common Mistakes",
        (
            "1.  Using Unstructured when Structured is supported\n"
            "    \u2192 Your A/R team ends up parsing free text manually\n\n"
            "2.  Omitting OriginalPaymentInformation\n"
            "    \u2192 Required (1..1). Missing = schema failure = rejected\n\n"
            "3.  Wrong namespace version\n"
            "    \u2192 Using remt.001.001.01 for a v06 message = rejected\n\n"
            "4.  More than 3 AddtlRmtInf elements\n"
            "    \u2192 Spec allows 0..3. A 4th element fails validation\n\n"
            "5.  Repeating RmtAmtAndTp with CREN / DUPA / REMI type code\n"
            "    \u2192 Explicit spec constraint. XSD will reject it."
        ),
        "Print this slide. These are the five bugs that will consume "
        "the first two sprints if not addressed up front. "
        "All five are directly derived from the ISO spec — not opinions."
    ),
    (
        "Next Steps",
        (
            "1.  Download the XSD\n"
            "      iso20022.org \u2192 remt.001.001.06.xsd\n\n"
            "2.  Scaffold the C# data containers\n"
            "    (available in the Beneficial Strategies ISO 20022 library)\n\n"
            "3.  Build a schema-validating serializer harness\n\n"
            "4.  Write test cases for:\n"
            "      \u2022  Happy path (structured, two invoices)\n"
            "      \u2022  Unstructured only\n"
            "      \u2022  CREN type code (no-repeat constraint)\n"
            "      \u2022  Missing OriginalPaymentInformation (expect rejection)\n\n"
            "5.  Ask the AI: 'Generate a full QA test plan for remt.001'\n"
            "    The MCP server will generate 50+ test cases automatically."
        ),
        "Step 5 is not a joke. Everything in this deck was generated automatically "
        "from the live ISO 20022 specification by the Beneficial Strategies MCP Server. "
        "Your QA plan can be too."
    ),
]

TITLE_COLOR   = RGBColor(0x00, 0x56, 0x4F)   # Beneficial Strategies dark teal
BODY_FONT     = "Calibri"
TITLE_FONT    = "Calibri"
SLIDE_W       = Inches(13.33)
SLIDE_H       = Inches(7.5)

prs = Presentation()
prs.slide_width  = SLIDE_W
prs.slide_height = SLIDE_H

blank_layout = prs.slide_layouts[6]   # fully blank

for title_text, body_text, note_text in SLIDES:
    slide = prs.slides.add_slide(blank_layout)

    # Title box
    txb = slide.shapes.add_textbox(Inches(0.4), Inches(0.25), Inches(12.5), Inches(1.1))
    tf  = txb.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = title_text
    p.alignment = PP_ALIGN.LEFT
    run = p.runs[0]
    run.font.name   = TITLE_FONT
    run.font.size   = Pt(32)
    run.font.bold   = True
    run.font.color.rgb = TITLE_COLOR

    # Horizontal rule (thin rectangle)
    rule = slide.shapes.add_shape(
        1,   # MSO_SHAPE_TYPE.RECTANGLE
        Inches(0.4), Inches(1.45), Inches(12.5), Inches(0.04))
    rule.fill.solid()
    rule.fill.fore_color.rgb = TITLE_COLOR
    rule.line.fill.background()

    # Body box
    txb2 = slide.shapes.add_textbox(Inches(0.4), Inches(1.6), Inches(12.5), Inches(5.4))
    tf2  = txb2.text_frame
    tf2.word_wrap = True
    tf2.auto_size = None

    first = True
    for line in body_text.split("\n"):
        p2 = tf2.paragraphs[0] if first else tf2.add_paragraph()
        first = False
        p2.text = line
        if p2.runs:
            r = p2.runs[0]
            r.font.name = BODY_FONT
            r.font.size = Pt(16)
            r.font.color.rgb = RGBColor(0x22, 0x22, 0x22)

    # Speaker notes
    notes_slide = slide.notes_slide
    notes_tf    = notes_slide.notes_text_frame
    notes_tf.text = note_text

prs.save("RemittanceAdvice-Training.pptx")
print("Done — RemittanceAdvice-Training.pptx is ready.")

Step 3: The file appears on your desktop. Claude reports:

Done — RemittanceAdvice-Training.pptx is ready.

You open it in PowerPoint or Google Slides. 12 slides. Speaker notes on every slide. Your branding colours. Ready to present.

No Python knowledge required. No compiling. No configuration.

What Claude Code Makes Possible

This is the distinction between Claude on the web and Claude Code running locally:

Claude (web) Claude Code (desktop)
Reads the ISO 20022 spec via MCP
Writes the Python script
Runs the script on your machine
Produces the actual .pptx file
Installs missing packages automatically

The manager asks one question. Claude Code queries the MCP server, reads the spec, writes the script, installs python-pptx if needed, runs it, and delivers a finished file — all without the manager touching a terminal or knowing what Python is.

That’s what a subscription to the Beneficial Strategies MCP Server does for your management team. Learn more about subscription tiers.


See It For Yourself

The presentation generated for this use case is available to download:

RemittanceAdvice-Training.pptx — 20-slide ISO 20022 RemittanceAdvice developer training deck covering business context, the ISO 20022 standard, full message structure deep dive, C# implementation, XML validation, the remt.002 companion message, and a real-world workflow — generated entirely by Claude Code using the Beneficial Strategies MCP Server.