I am writing an IC app that persists immutable invoice information - I want to write it to the IC blockchain and also retrieve it knowing that it is always accessible and cannot be changed. It appears that data held in Canisters can be updated or deleted as it is held in memory - is that correct?
Its kind of both, you can make the data updatable/deletable as well as you can make it immutable.
So in a canister you can define stable data and then deploy it as a controller. And then remove yourself from the controller list. Now the data which you defined inside a canister becomes immutable, you cannot change the data inside it, you cannot change a single line of code inside, vice versa is also possible if you keep your self as a controller.
For examples how to read/write to a canister you can start from motoko docs and examples repos from dfinity offical github.
The ICP ledger’s purpose it to store ICP token transactions. Note that it also creates a chain of blocks in the canister itself, with one transaction per block.
I wouldn’t recommend to abuse the ICP ledger canister to store arbitrary data.
To add a bit more nuance, the IC is not a traditional blockchain, in that it only relies on a blockchain for consensus (all inputs are placed in blocks; these blocks are then the input to a deterministic state machine); but this blockchain is not persisted past a checkpoint (which happens every 500 blocks or just under 10 minutes, on average).
In this respect, every subnet is a replicated virtual machine and all state is held within this virtual machine (in the canister’s heap or stable memory; transparently backed by persistent storage via orthogonal persistence). Meaning that if you want to provide provable immutability, you need to rely on mechanisms within the protocol (e.g. publishing the code of your canister and giving up all control of it; or having your canister build a blockchain of all the transactions it executes, as does the ICP ledger).