# SWIFTFramework 2026.2 — LLM / AI working guide

This file is the contract for AI assistants generating or editing code that uses SWIFTFramework. Follow it exactly. Human docs live in docs/*.md.

## Product

- Library: SWIFTFramework.dll, strong-named, assembly version 2026.2.724
- Target: net10.0 (.NET 10). No extra NuGet dependencies.
- Purpose: parse, create, validate, and serialize SWIFT FIN MT messages as typed C# objects.
- 291 typed MT/MTS classes in categories 0–9.
- XML comments: SWIFTFramework.xml next to the DLL.

## Hard rules

1. Set Licensing.RunTimeLicenseKey BEFORE any SwiftMessage / MT* construction (including parser.GetMessage).
2. This build accepts product codes SWF-26-20, SWF-26-21, SWF-26-22, SWF-26-23, SWF-26-24 only. Older SWF-26-1x keys fail.
3. Pass only the hex key (HHHH-HHHH-…). Never pass the generator prefix SWF-26-24-SL-….
4. Missing/invalid key => System.ComponentModel.LicenseException on SwiftMessage construction.
5. SWIFT amounts use comma decimal: 14551, or 14551,00. Prefer typed Amount/Currency/Date properties.
6. Dates in 32A-style tags are yyMMdd.
7. Namespaces: SWIFTFramework.Messages.CategoryN with NO space. Folder names have a space; C# does not.
8. MT class = first digit of type. MT103 => Category1. MT012 => Category0. MT300 => Category3.
9. PLUS/REMIT/COV are separate classes: MT103PLUS, MT103REMIT, MT202COV, MT205COV.
10. Do not invent MT types that are not in the list below.
11. Do not use licenses.licx. Do not call WinForms license dialogs (not in this build).
12. ValidationErrorType.UsupportedBlockFormat is spelled that way in the public API — do not “fix” it.
13. Never hard-code a purchased key. Samples load SWIFTFRAMEWORK_RUNTIME_LICENSE_KEY or User Secrets.

Run samples (no key is shipped):

```bash
export SWIFTFRAMEWORK_RUNTIME_LICENSE_KEY='your-purchased-key'
dotnet run --project Framework/SourceCode/ConsoleSample/ConsoleSample.csproj
```

From this release package:

```bash
export SWIFTFRAMEWORK_RUNTIME_LICENSE_KEY='your-purchased-key'
dotnet run --project samples/ParseAndModify/ParseAndModify.csproj
```

## Minimal working program

```csharp
using SWIFTFramework;
using SWIFTFramework.Messages.Category1;
using SWIFTFramework.Validation;

Licensing.RunTimeLicenseKey = Environment.GetEnvironmentVariable("SWIFTFRAMEWORK_RUNTIME_LICENSE_KEY");
// Never hard-code a purchased key in samples or public source.

var parser = new SwiftParser();
var errors = new List<ValidationError>();
SwiftMessage message = parser.GetMessage(finText, errors);

var mt103 = (MT103)message;
Console.WriteLine(mt103.SendersReference_20.Value);
mt103.Tag59_BeneficiaryCustomer.Value = "/1122334455";
mt103.ValueDateCurrencyInterbankSettledAmount_32A.Date = DateTime.Today;
string updated = parser.GetMessageAsText(mt103);
```

Project reference:

```xml
<ItemGroup>
  <Reference Include="SWIFTFramework">
    <HintPath>lib/SWIFTFramework.dll</HintPath>
  </Reference>
</ItemGroup>
```

TargetFramework: net10.0

## Licensing API

```csharp
SWIFTFramework.Licensing.RunTimeLicenseKey = key; // get/set string
SWIFTFramework.Licensing.GetLicensingPath();      // returns "SWIFTFramework.2021-01.lic" (legacy filename helper)
```

If unset, first access reads .NET User Secrets id "SWIFTFramework":
- Windows: %APPDATA%\Microsoft\UserSecrets\SWIFTFramework\secrets.json
- Unix: ~/.microsoft/usersecrets/SWIFTFramework/secrets.json
JSON keys accepted: "SWIFTFramework:RuntimeLicenseKey", nested SWIFTFramework.RuntimeLicenseKey, or "RuntimeLicenseKey".

## Parser API

```csharp
public sealed class SwiftParser
{
    public SwiftParser();
    public static SwiftParser Instance { get; }

    public SwiftMessage GetMessage(string message, List<ValidationError> parseError, bool validate = true);
    public List<SwiftMessage> GetMessages(string messages, char delimiter, List<ValidationError> parseError, bool validate = true);

    public string GetMessageAsText(SwiftMessage message);
    public string GetMessageAsXml(SwiftMessage message);      // structured; throws if definition mismatch
    public string GetMessageAsXmlRaw(SwiftMessage message);   // blocks+tags only
    public SwiftMessage GetMessageFromXml(string xml);        // accepts structured or raw
}
```

GetMessage flow: parse FIN -> SwiftMessageFactory.CreateMessage (typed subclass) -> optional Validate.

## SwiftMessage API

```csharp
public class SwiftMessage : SwiftElement
{
    public SwiftMessage();
    public SwiftMessage(bool initBlocks);
    public SwiftMessage(SwiftMessage message);

    public SwiftBlock1 Block1 { get; set; }
    public SwiftBlock2 Block2 { get; set; }   // Input or Output subclass
    public SwiftBlock3 Block3 { get; set; }
    public SwiftBlock4 Block4 { get; set; }
    public SwiftBlock5 Block5 { get; set; }
    public List<SwiftBlockUser> UserBlocks { get; set; }

    public virtual string MessageType { get; }   // "103"
    public string Content { get; }               // original FIN if parsed

    public virtual bool IsMTType(string type);   // type must be length 3 or throws
    public SwiftTag GetTagByName(string tagName);     // first Block4 match
    public SwiftTag GetTagByNumber(int tagNumber);

    public virtual bool Validate();
    public virtual bool Validate(List<ValidationError> errorList);

    public string ToXml();
    public string ToXmlRaw();
    public string ToXml(SwiftMessageExportType exportType); // Structured | Raw | Biztalk
}
```

## Blocks

Block1: ApplicationIdentifier (F|A|L), ServiceIdentifier, LTAddress (12 chars), SessionNumber, SequenceNumber.

Block2Input (you send): MessageType, MessagePriority (S|U|N), DestinationAddress, DeliveryMonitoring, ObsolescencePeriod.
Cast: ((SwiftBlock2Input)msg.Block2).DestinationAddress = "BANKUKZHAXXX";

Block2Output (you received): MessageType, MessagePriority, InputTime, MessageInputReference, ReceiverOutputDate, ReceiverOutputTime.

Block3/4/5/User: tag lists. AddTag(new SwiftTag(name, value)).

new SwiftTag("20:VALUE") or new SwiftTag("20", "VALUE").

## Tag property convention on typed MT classes

Every field has two getters that wrap the same tag:

- Semantic_TagNumber          e.g. SendersReference_20
- TagNumber_Semantic          e.g. Tag20_SendersReference

Repeating tags: T[] plus AddSemantic_Tag() factory.
Sequences: SequenceA / SequenceB / … with properties named Semantic_Seq_Tag (e.g. SequenceA.TransactionReferenceNumber_A_20).

Typed wrappers (Tag32A, Tag33B, …) expose:
- Value (raw SWIFT)
- Date (DateTime, yyMMdd) when present
- Currency (string) when present
- Amount (decimal) when present

Example create:

```csharp
var mt103 = new MT103();
mt103.Block1.ApplicationIdentifier = "F";
mt103.Block1.ServiceIdentifier = "01";
mt103.Block1.LTAddress = "BANKUSPPAXXX";
mt103.Block1.SessionNumber = "0000";
mt103.Block1.SequenceNumber = "000000";
mt103.Block2.MessageType = "103";
mt103.Block2.MessagePriority = "N";
((SwiftBlock2Input)mt103.Block2).DestinationAddress = "BANKUKZHAXXX";
mt103.SendersReference_20.Value = "ABCDEREF";
mt103.BankOperationCode_23B.Value = "CRED";
mt103.ValueDateCurrencyInterbankSettledAmount_32A.Amount = 14551m;
mt103.ValueDateCurrencyInterbankSettledAmount_32A.Currency = "USD";
mt103.ValueDateCurrencyInterbankSettledAmount_32A.Date = new DateTime(2026, 7, 24);
mt103.OrderingCustomer_50K.Value = "/0123456789";
mt103.BeneficiaryCustomer_59.Value = "/9988776655";
mt103.DetailsOfCharges_71A.Value = "SHA";
string fin = SwiftParser.Instance.GetMessageAsText(mt103);
```

Choice options (50A/50F/50K, 59/59A, 52A/52D, …) are separate properties. Set only the option you want to emit.

COV: new MT202COV() / new MT205COV() — writer emits {119:COV} in Block 3.

## Validation

```csharp
namespace SWIFTFramework.Validation
{
    public class ValidationError
    {
        public ValidationErrorType Type { get; set; }
        public string Description { get; set; }
        public object ParentPropertyObject { get; set; }
        public string PropertyName { get; set; }
    }
    public enum ValidationErrorType
    {
        Unknown, UnsupportedValue, UsupportedBlockFormat, UnrecognizedValue,
        WrongLength, WrongRange, MissingBlock1, MissingBlock2, MissingBlock4,
        MissingBlock5, UserError
    }
}
```

Print Type and Description. GetMessage(..., validate:false) skips definition validation.

## XML

- ToXml() / GetMessageAsXml: structured, type-named elements. Throws InvalidOperationException if structure does not match definition.
- ToXmlRaw() / GetMessageAsXmlRaw: blocks and tags only; works on imperfect messages.
- ToXml(SwiftMessageExportType.Biztalk): BizTalk schema shape.
- GetMessageFromXml(xml): import structured or raw.

Structured MT103 root: ns0:SWIFT_CATEGORY1_MT103_Interchange xmlns:ns0="http://swiftcomponents.com/Category1/MT103"

## FIN sample (MT103)

```
{1:F01BANKUSPPAXXX6453938350}{2:O1030805090112BANKUKZHH80A53405362810901120805N}{4:
:20:ABCDEREF
:23B:CRED
:32A:090113USD14551,
:33B:USD14551,
:50K:/0123456789
:52A:BANKUSPP
:57A:/9876543210
BANKUKZH
:59:/9988776655
:71A:SHA
-}{5:{CHK:30860BAE4D8E}{MAC:00000000}}{S:{SAC:}{COP:P}}
```

## Namespaces cheat sheet

```csharp
using SWIFTFramework;
using SWIFTFramework.Validation;
using SWIFTFramework.Messages.Category0; // system
using SWIFTFramework.Messages.Category1; // payments
using SWIFTFramework.Messages.Category2; // FI transfers
using SWIFTFramework.Messages.Category3; // FX / MM
using SWIFTFramework.Messages.Category4; // collections
using SWIFTFramework.Messages.Category5; // securities
using SWIFTFramework.Messages.Category6; // metals
using SWIFTFramework.Messages.Category7; // trade finance
using SWIFTFramework.Messages.Category8; // travellers cheques
using SWIFTFramework.Messages.Category9; // cash management
```

## Complete MT / MTS class list

Category0: MT008 MT009 MT010 MT011 MT012 MT015 MT019 MT020 MT021 MT022 MT023 MT028 MT029 MT031 MT032 MT035 MT036 MT037 MT041 MT042 MT043 MT044 MT045 MT046 MT047 MT048 MT049 MT051 MT052 MT055 MT056 MT057 MT061 MT062 MT063 MT064 MT065 MT066 MT067 MT068 MT069 MT070 MT071 MT072 MT073 MT074 MT075 MT076 MT077 MT081 MT082 MT083 MT085 MT087 MT090 MT092 MT094 MT096 MT097 MTS02 MTS03 MTS05 MTS06 MTS12 MTS13 MTS14 MTS15 MTS21_FIN_ACKNAK MTS22 MTS23 MTS25 MTS26 MTS33 MTS35 MTS42 MTS43

Category1: MT101 MT102 MT102PLUS MT103 MT103PLUS MT103REMIT MT104 MT105 MT107 MT110 MT111 MT112 MT190 MT191 MT192 MT195 MT196 MT198 MT199

Category2: MT200 MT201 MT202 MT202COV MT203 MT204 MT205 MT205COV MT210 MT290 MT291 MT292 MT293 MT295 MT296 MT298 MT299

Category3: MT300 MT304 MT305 MT306 MT320 MT321 MT330 MT340 MT341 MT350 MT360 MT361 MT362 MT364 MT365 MT370 MT380 MT381 MT390 MT391 MT392 MT395 MT396 MT398 MT399

Category4: MT400 MT410 MT412 MT416 MT420 MT422 MT430 MT450 MT455 MT456 MT490 MT491 MT492 MT495 MT496 MT498 MT499

Category5: MT500 MT501 MT502 MT503 MT504 MT505 MT506 MT507 MT508 MT509 MT510 MT513 MT514 MT515 MT516 MT517 MT518 MT519 MT524 MT526 MT527 MT530 MT535 MT536 MT537 MT538 MT540 MT541 MT542 MT543 MT544 MT545 MT546 MT547 MT548 MT549 MT558 MT559 MT564 MT565 MT566 MT567 MT568 MT569 MT575 MT576 MT578 MT581 MT586 MT590 MT591 MT592 MT595 MT596 MT598 MT599

Category6: MT600 MT601 MT604 MT605 MT606 MT607 MT608 MT620 MT670 MT671 MT690 MT691 MT692 MT695 MT696 MT698 MT699

Category7: MT700 MT701 MT705 MT707 MT708 MT710 MT711 MT720 MT721 MT730 MT732 MT734 MT740 MT742 MT744 MT747 MT750 MT752 MT754 MT756 MT759 MT760 MT767 MT768 MT769 MT790 MT791 MT792 MT795 MT796 MT798 MT799

Category8: MT800 MT801 MT802 MT824 MT890 MT891 MT892 MT895 MT896 MT898 MT899

Category9: MT900 MT910 MT920 MT935 MT940 MT941 MT942 MT950 MT970 MT971 MT972 MT973 MT985 MT986 MT990 MT991 MT992 MT995 MT996 MT998 MT999

## Recipes for agents

Parse unknown type:
```csharp
SwiftMessage m = parser.GetMessage(text, errors);
switch (m.MessageType) {
  case "103": var p = (MT103)m; break;
  case "202": /* could be MT202 or MT202COV — use m is MT202COV */ break;
  default: break;
}
if (m is MT202COV cov) { /* cover */ }
```

Validate and reject:
```csharp
var errors = new List<ValidationError>();
var m = parser.GetMessage(text, errors);
if (errors.Count > 0) throw new InvalidOperationException(string.Join("\n",
    errors.Select(e => $"{e.Type}: {e.Description}")));
```

XML round-trip:
```csharp
string xml = message.ToXml();
SwiftMessage again = parser.GetMessageFromXml(xml);
string fin = parser.GetMessageAsText(again);
```

Do not:
- Reference SWIFTFramework.Licensing types other than Licensing (EncryptedLicenseProvider is internal).
- Assume WPF/WinForms — the library is net10.0, not net10.0-windows.
- Use System.Decimal with a period when writing raw tag Value strings.
- Call IsMTType("103PLUS") — MessageType is the 3-digit FIN type; PLUS/REMIT are CLR types.

## Human docs map

- docs/getting-started.md
- docs/licensing.md
- docs/parsing-and-writing.md
- docs/creating-messages.md
- docs/validation.md
- docs/xml.md
- docs/api-reference.md
- docs/message-types.md
- samples/ParseAndModify, samples/CreateMessage, samples/XmlRoundTrip (no keys shipped)
