Message Envelopes
Carry routing headers across a conversion: FIN blocks 1-3 on MT messages and the ISO 20022 Business Application Header (AppHdr) on MX messages, with the envelope option on convert and normalize.
What an envelope is
A financial message travels inside a transport wrapper that says who sent it, who receives it and what it is. transmute calls that wrapper the envelope, and handles two kinds:
| Kind | Format | Carries |
|---|---|---|
| FIN header blocks | MT. The {1:}, {2:} and {3:} blocks in front of the {4:} text block. | Sender and receiver addresses (BIC-based), message type, priority, and in block 3 the Message User Reference ({108:}) and the UETR ({121:}) |
| Business Application Header (AppHdr) | MX. An ISO 20022 head.001 document that travels next to the business Document. | Sender (Fr) and receiver (To) BICs, business message id (BizMsgIdr), message definition (MsgDefIdr), creation time (CreDt), priority (Prty) |
A BIC is the 8- or 11-character bank identifier code (AAAADEFF). A UETR is the end-to-end tracking reference of a payment.
Envelope handling is opt-in. By default (envelope=none) transmute converts the business message only, and its output and warnings do not depend on envelope data at all.
Envelopes on input
Envelopes never get in the way of parsing. An MT message may arrive with or without blocks 1 to 3. An MX message is accepted in any of these shapes:
- a bare
Document; - an
AppHdrand aDocumentside by side, either inside a wrapper element or as two top-level elements one after the other (as some gateways send them); - a wrapping envelope such as
BizMsgEnvlp, with theAppHdrandDocumentnested inside.
transmute finds the first Document in an ISO 20022 namespace; everything around it is envelope. The AppHdr is read in every head.001 version, and fields it does not model are ignored.
/v1/validate at level=schema also checks a head.001.001.02 AppHdr that travels with the Document: the required Fr, To, BizMsgIdr, MsgDefIdr and CreDt, and the BIC and date-time shapes. Its findings are prefixed AppHdr.. See Validate.
The envelope option
| Endpoint | Values | Effect |
|---|---|---|
/v1/convert | none (default) | Business message only |
apphdr | MX targets. Emit a head.001.001.02 AppHdr built from the source's envelope, then the Document | |
fin | MT targets. Wrap every output message in FIN blocks 1 and 2 built from the source's envelope | |
/v1/normalize | none (default), include | include adds the source envelope to the normalized JSON as result.envelope |
Send it as options.envelope in a JSON envelope, as ?envelope= on raw bodies, or as the X-Transmute-Envelope header (in that order of precedence).
These are invalid-input 400s, because routing data is never invented:
- a value that does not fit the target, for example
envelope=apphdrwithto=mt103orenvelope=finwith an MX target; - a source that does not carry both a sender and a receiver BIC (no FIN blocks 1 and 2 on an MT source, no AppHdr on an MX source).
{
"type": "https://docs.transmute.403fin.io/errors/invalid-input",
"title": "Invalid input",
"status": 400,
"detail": "Envelope option cannot be applied: envelope=fin needs the source to carry sender and receiver BICs (FIN blocks 1 and 2 for MT, an AppHdr for MX)",
"instance": "/v1/convert"
}
MT in, AppHdr out (envelope=apphdr)
curl -sS -X POST "https://api.transmute.403fin.io/v1/convert?to=pacs.008&envelope=apphdr" \
-H "Authorization: Bearer $TRANSMUTE_KEY" \
-H "Content-Type: text/plain" \
--data-binary @- <<'MSG'
{1:F01AAAADEFFAXXX0000000000}{2:I103BBBBDEFFXXXXN}{3:{108:MUR-DOCS-01}{121:00000000-0000-4000-8000-000000000103}}{4:
:20:ENVDOCS001
:23B:CRED
:32A:261002EUR250,00
:50K:/DE89370400440532013000
MAX EXAMPLE
:59:/GB29NWBK60161331926819
ERIKA EXAMPLE
:71A:SHA
-}
MSG
result holds two top-level elements, the AppHdr and then the Document. transmute never wraps them in a BizMsgEnvlp. Shortened:
<?xml version="1.0" encoding="UTF-8"?>
<AppHdr xmlns="urn:iso:std:iso:20022:tech:xsd:head.001.001.02"><Fr><FIId><FinInstnId><BICFI>AAAADEFF</BICFI></FinInstnId></FIId></Fr><To><FIId><FinInstnId><BICFI>BBBBDEFF</BICFI></FinInstnId></FIId></To><BizMsgIdr>ENVDOCS001</BizMsgIdr><MsgDefIdr>pacs.008.001.08</MsgDefIdr><CreDt>2026-10-08T10:56:57Z</CreDt><Prty>NORM</Prty></AppHdr><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">...</Document>
How the AppHdr is filled:
| AppHdr element | Comes from |
|---|---|
Fr / To | Sender and receiver BICs from blocks 1 and 2 |
BizMsgIdr | The MT :20: reference. Absent: NOTPROVIDED with MISSING_REQUIRED_DEFAULTED. Longer than 35 characters: TRUNC_REFERENCE. |
MsgDefIdr | Always the Document actually rendered (pacs.008.001.08 here) |
CreDt | The source envelope's creation time. An MT header has none, so it is the request time with MISSING_REQUIRED_DEFAULTED (info). |
Prty | Block 2 priority: N becomes NORM, U becomes HIGH |
The {108:} Message User Reference has no AppHdr slot, so it is reported as ENVELOPE_DROPPED (info). The UETR still reaches the Document through the payment mapping, as it does without an envelope.
AppHdr in, MT out (envelope=fin)
Send the AppHdr and Document produced above to to=mt103&envelope=fin and the MT output gets FIN blocks 1 and 2:
{1:F01AAAADEFFAXXX0000000000}{2:I103BBBBDEFFAXXXN}{3:{121:00000000-0000-4000-8000-000000000103}}{4:
:20:ENVDOCS001
...
-}
| FIN element | Comes from |
|---|---|
| Block 1 address | The AppHdr sender BIC, as a 12-character terminal address |
| Block 2 destination | The AppHdr receiver BIC |
| Block 2 message type | The MT target |
| Block 2 priority | NORM becomes N; HIGH and URGT become U; anything else is N with ENVELOPE_DROPPED |
| Block 1 session and sequence numbers | Zero placeholders (0000, 000000) with MISSING_REQUIRED_DEFAULTED. Your interface assigns the real numbers when it sends the message. |
| Block 3 | Kept when the MT renderer writes one (the {121:} UETR of an MT103) |
The AppHdr BizMsgIdr and CreDt have no slot in an input FIN header, so each is reported as ENVELOPE_DROPPED (info). The :20: reference in the text block still carries the transaction reference. When MT paging splits the output, every page gets the envelope.
Normalize with the envelope (envelope=include)
curl -sS -X POST "https://api.transmute.403fin.io/v1/normalize?envelope=include" \
-H "Authorization: Bearer $TRANSMUTE_KEY" \
-H "Content-Type: text/plain" \
--data-binary @mt103.txt
The envelope arrives as result.envelope, beside the business document and never inside it:
{
"result": {
"type": "payment",
"payment": { "...": "..." },
"envelope": {
"senderBic": "AAAADEFF",
"receiverBic": "BBBBDEFF",
"messageId": "ENVDOCS001",
"messageDefinition": "MT103",
"priority": "NORM",
"mur": "MUR-DOCS-01",
"uetr": "00000000-0000-4000-8000-000000000103"
}
},
"warnings": [],
"meta": { "...": "..." }
}
| Member | MT source | MX source |
|---|---|---|
senderBic / receiverBic | Blocks 1 and 2 | AppHdr Fr / To |
messageId | :20: | BizMsgIdr |
messageDefinition | MT + message type | MsgDefIdr |
createdAt | (none) | CreDt, as a date-time with an explicit offset |
priority | NORM, HIGH, or the block 2 code as sent | Prty |
mur | Block 3 {108:} | (none) |
uetr | Block 3 {121:} | (none) |
Every member is optional and omitted when empty. A source without an envelope gives no result.envelope.
If you send the normalized JSON back with from=canonical, result.envelope feeds envelope=apphdr and envelope=fin exactly like a native envelope. See Normalize.
Envelope warnings
| Code | Severity | Raised when |
|---|---|---|
ENVELOPE_MISMATCH | warning | An incoming AppHdr's MsgDefIdr names a different message than the namespace of the Document it travels with. Read only when an envelope option is set. |
ENVELOPE_DROPPED | info | An envelope component (the MUR, BizMsgIdr, CreDt, a priority with no counterpart, an AppHdr BizSvc) has no slot in the envelope you asked for, or an incoming AppHdr value is not in a usable form |
{ "code": "ENVELOPE_MISMATCH", "severity": "warning",
"sourceField": "AppHdr/MsgDefIdr", "targetField": "messageDefinition",
"detail": "AppHdr message definition (length 15) does not match the Document namespace" }
Envelope warnings, like all warnings, name fields and lengths only, never values. The full list is in the warning registry.