The unique identifier for a physical volume is the copy number (accessed via GetCopyNo(), GetCopyNumber(), or GetReplicaNumber() depending on which class you happen to have a pointer for, G4VPhysicalVolume or G4VTouchable).
If you are doing the emplacements “manually” (i.e., with G4PVPlacement) the way you described, you cannot get globally unique copy numbers for your SiPMs, as you already know. In what follows, I will assume that you’ve assigned unique copy numbers to each SiPM placed with the scintillator assembly volume, and also assigned unique copy numbers to each of the scintillator assembly placements.
What you can do at tracking time is make use of the G4Touchable from the G4Step. From touchable->GetCopyNumber(), you can get the SiPM’s copy number, iSiPM. Then you can go up the placement tree, what’s called the touchable’s “history”, and get touchable->GetCopyNumber(1) for the copy number of the SiPM’s parent volume, touchable->GetCopyNumber(2) for the grandparent volume, and so on. If the SiPM’s are placed directly into your scintillator assembly, then iScint = touchable->GetCopyNumber(1) is the scintillator’s copy number.
So now you can construct your unique ID as iScint6 + iSiPM, or iScint100+iSiPM for human readability, or whatever you like. You don’t need to do this after the geometry has been closed; you can do it in your stepping action, or sensitive detector, or whatever.