Maximilian Beier scored twice for the visitors to come from behind and end a run of eight games without a win.
Dortmund remained fourth — in the last place that guarantees Champions League qualification — with Leipzig a point behind.
Ihlas Bebou shocked Dortmund in the second minute after a shaky start from defenders Can and Niko Schlotterbeck. Bebou pounced on the ball between them and scored from a difficult angle.
But the home team didn’t panic. Marco Reus set up Donyell Malen to equalize in the 21st, four minutes before he sent in a free kick for Schlotterbeck to head Dortmund’s second goal.
Hoffenheim equalized through Beier on a counterattack in the 61st, three minutes before he grabbed his second goal and prompted the first chorus of whistles.
Dortmund’s penalty appeals when Marcel Sabitzer’s shot was blocked by Florian Grillitsch’s arm were waved away after a VAR check, and Can went on to miss the best chance to equalize in injury time.
Earlier, Dortmund’s “Ultra” fans celebrated the German soccer league’s midweek decision to scrap its controversial plan to share clubs’ future media rights income with an outside investor for an upfront payment. The fans’ banner read, “Game, set and match – the winner is football.”
There was a related protest in Frankfurt earlier when Eintracht Frankfurt fans targeted Volkswagen-backed Wolfsburg over German soccer’s 50-plus-1 rule against outside investors.
The match ended 2-2 with Omar Marmoush scoring Frankfurt’s equalizer in stoppage time.
Freiburg visited Augsburg later Sunday.