Yeah, probably is your best option.

Anyway, I think that you could add the pieces of the base64 field as values of the Base64EncodedFile list removing the (1) in my code:

Do stream.Rewind()
While (stream.AtEnd = 0) {
    // Not sure about the len
    Set len = 3641144
    do target.SetValueAt(stream.Read(.len), "LetterAttachment(1).Base64EncodedFile", "append")
}

The problem of this is that you couldn't include more documents attached because all the list would belong to just one document.

I was reviewing the documentation and I think that the only option available is to split your base64 by the MAXSTRING limit and append all the pieces that you get using SetValueAt defining the action as append:

method SetValueAt(pValue As %String, 
                  pPropertyPath As %String, 
                  pAction As %String = "set", 
                  pKey As %String = "") as %Status

It could be something like:

Do stream.Rewind()
While (stream.AtEnd = 0) {
    // Not sure about the len
    Set len = 3641144
    do target.SetValueAt(stream.Read(.len), "LetterAttachment(1).Base64EncodedFile(1)", "append")
}

We can see here the differences between R4 and R5 releases of FHIR. I can't imagine an organization migrating FHIR respositories every 3 or 4 years to keep updated to the latest version. What I forsee it's a future with a lot of organizations using different versions of FHIR and trying to communicate among them struggling against the differences...exactly the same that we have right now.