There are many posts about creating Case threadIds with Apex, notably Case.Thread_Id, is Thread_Id a field on Case object?
As the posters have noted, the SFDC rules of the threadId are subject to change over time and the only reliable way that I've seen is to do this:
// To be replaced with a real case Id.
Id caseId = '500c0000008kJL5';
List<Messaging.RenderEmailTemplateBodyResult> renderResults =
Messaging.renderEmailTemplate(null, caseId, new List<String>{'{!Case.Thread_Id}'});
System.debug('this is the Case Thread Id: ' + renderResults[0].getMergedBody());
HOWEVER ...
The method call Messaging.renderEmailTemplate
burns a SOQL (see doc)
So you can't prepopulate your Cases in an afterInsert or beforeUpdate Trigger because a transaction can be up to 2,000 (Platform Events), up to 10,000 (direct Apex DML) or more, typically, 200 rows. All of these are > 100 SOQL limit.
So, before I go to the trouble of doing this in a chained set of async tasks; is there more recent thinking on this subject of creating ThreadIds?
Messaging.renderEmailTemplate
no longer generates a threadID.