A police detective reconnects with people from an undercover assignment in her distant past in order to make peace.
Detective Erin Bell (Nicole Kidman) receives a ink-stained dollar in the mail, a message from her old enemy, Silas. Sixteen years ago Erin went undercover into his gang of bank robbers and he killed her partner and lover, Chris (Sebastian Stan) shortly after she discovered she was pregnant. She makes her way through old members of their gang until she finds Silas, and kills him.
It is revealed that that Silas’s body is the one Erin was called to the scene of at the opening of the movie. As the story loops back around to the beginning, Erin, having been brutally injured in her quest to find Silas, goes back to her car and dies, having gotten her revenge and also after having started to apologize to her daughter for the tough live she’s lived.
Detective Erin Bell (Nicole Kidman) shows up to a crime scene, ragged and rough. There’s a dead shooting victim with a tattoo of three dots on the back of his neck, purple-dye stained dollar bills around him. The cops on the scene say it’s their case and tell her scram, but Bell tells them she knows who did it. She returns to her car and sits down, exhausted.
At the police station, her boss tells her she’s received mail at the precinct. When she opens it, there’s a purple-dye stained bill inside. Someone has sent her a message. Angered, she goes to the FBI office to meet her old supervisor, Gil Lawson (Toby Huss). They catch up, and she asks him if he can trace the bill she received in the mail. He wonders if she really wants to go down this path, but he insists. He confirms it’s a match. Erin leaves, ready for a fight. We see that she has tattoo removal scars for three dots on the back of her neck.
In flashback, a much younger and healthier looking Erin meets Chris (Sebastian Stan). The two of them are being sent undercover to bust up a gang that’s committing bank robberies. They go over their covers meticulously, and since they will be posing as a couple, they try out a kiss.
Back in present day, Erin contacts a prison to get a meeting with Toby (James Jordan), an incarcerated member of the gang but is informed that he has been given compassionate release since he’s dying. She drives out to his home and finds him decrepit and in bed, dying fast. She tells him she wants to find Silas (Toby Kebbell), the leader of the gang. He claims he doesn’t know where she is but says he knows where she can find Arturo (Zach Villa), another gang member who’s on the run. He won’t tell Erin unless she gives him a handjob. She at first refuses but then does, disgustedly. Toby tells Erin that Arturo hides out in a church where he provides free legal advice to the needy, his way of giving penance.
In a flashback, we see Silas, a sociopathic leader of the gang, forcing Arturo to play Russian Roulette in front of the whole group. At this point, Erin and Chris’s pretend romantic relationship has become a totally real one. In the present day, Erin goes to the church but finds it closed. She goes to have a drink and finds her own teenage daughter, Shelby (Jade Pettyjohn) there with her skeevy older boyfriend Jay (Beau Knapp). Erin tries to get Shelby out of there, but Shelby rejects her mother and Erin ends up hitting Jay, getting her kicked out of the bar.
Erin goes to her ex-boyfriend Ethan (Scoot McNairy), who Shelby lives with, and tells him he needs to do a better job of getting Shelby under control, but he points out that she’s not doing much to help Erin at all. The next day, Erin returns to the church, posing as a woman looking for help with a friend having immigration issues. She is led to Arturo, but once he sees her, he makes a run for it. Erin chases after him for a long while and finally catches him. She says she’s not after him, just Silas. Arturo doesn’t know where he is but knows who the attorney is that helps Silas keep getting his hands on the money from his robberies.
Erin goes to the extremely wealthy home of DiFranco (Bradley Whitford), the attorney. She tells him she knows he’s helping Silas, but he mercilessly mocks her, saying it’s her fault that Chris and a poor bank teller died in her undercover operation all those years ago. Erin goes to hit him, but DiFranco’s security guard comes from behind and beats her viciously in the stomach and takes her gun. Erin crawls into the bathroom, and when the security guard tries to get her out, she gets the jump on him, beating him unconscious, getting her gun back, and marching outside where DiFranco is coaching his son in a batting cage. She pistol-whips him until he’s bloody, and he confesses he knows when the drop is going to be. He tells her it’s not going to be Silas doing the pickup: it’s always Petra (Tatiana Maslany), Silas’s longtime girlfriend.
Erin goes to the drop and sees the once-beautiful, and vibrant Petra is now completely strung out on drugs. She follows Petra’s vehicle, hoping to catch Silas. Her current partner, Antonio (Shamier Anderson) has been continuously trying to reach her, but she brushes him off, saying she’s on her own business. She follows Petra and sees her meeting with a bunch of men and putting on masks and loading up with guns – she realizes they’re about to commit another bank robbery. She radios it in, pulls a shotgun out of her trunk, and along with two local cops who arrive on the scene, busts into the bank and stops the robbery in progress. A massive firefight breaks out, and Petra is hit in the leg but escapes. Erin allows the police to chase after her in the wrong direction, and follows Petra to another store, where she attempts to handcuff her. Petra fights back, getting in several brutal kicks to Erin’s midsection, the same spot where she was beaten by DiFranco’s bodyguard. Erin gets the upper hand and throws Petra into her trunk. She gets Petra away and handcuffs her. Petra is detoxing from drugs and begs for Erin to let her go, but Erin leaves her for the police to find – but not before demanding to know where she’s planning to meet Silas. Petra tells her she never knows, Silas will text her, so Erin takes Petra’s phone.
Shelby has ended up in the hospital with a busted hand after getting into an altercation while out at a bar with Jay, so Erin goes to Ethan and tells him she wants him to take Shelby and move out of town somewhere else and start a new life. She promises to get him money – lots of it. In flashback, Erin realizes she is pregnant with Chris’s baby. The two of them plan to go through with the robbery and keep some of the money for themselves to get out of the undercover life and the police life. They go over every contingency for how they’ll explain it all to the police after it happens.
In the present, Erin goes to a storage unit where she has kept a bag of money that has hidden away from the robbery all those years ago. However, when she opens it, she finds much of it has the purple dye over it, rendering it unusable. She screams in frustration. She takes the little amount of the money that is usable and meets up with Jay. She offers him the money to move away and never contact Shelby again. He is disgusted but ultimately accepts the money.
In flashback, we finally see the bank robbery. Everything is going according to plan, and the vicious Silas tells the frightened bank teller she better not put any dye in the money she’s bagging. The gang leaves with the money, but outside the bank a dye pack explodes, ruining an entire duffel of bills. Silas goes back in to murder the girl, and Chris, despite being able to make a run for it, turns back around to stop him. He identifies himself as an agent, but Silas shoots him and shoots the bank teller. When Erin sees Chris doesn’t leave, she is devastated, and takes off driving. Toby is in her car and screams at her demanding to know if she’s a cop. To escape him, she crashes her van, incapacitating Toby who she handcuffs. She takes her duffel of money and cries for Chris.
Erin has dinner with Shelby, who recounts a memory of being a little girl and being pulled out of school by her mom and taken camping in the middle of nowhere and how erratic it was. Erin tells Shelby that she’s sorry for everything, that it’s her fault that her father died. Erin insists Ethan is the only father that matters to her. Erin tells Shelby she knows she’s been a bad mother and she’s sorry, and that she loves her, and maybe someday she’ll understand. She kisses her on the forehead and leaves, finding a text from Silas on Petra’s phone.
Erin goes to meet Silas at the drop point. Silas is amused to see her and asks if she got his message he sent her (the dyed dollar she got at the precinct). Erin shoots and kills Silas, tossing dye-stained bills around him. We see the next day, the police investigating Silas’s body – HE was the corpse at the beginning of the film, which is actually also the end of the film. When Erin tells the cops she knows who did it, this time we know it was her. Antonio wants to know what the hell she’s been up to, and Erin gives him a note with an address and tells him he’ll be able to put it together. She goes out to her car, the scene we saw at the beginning, and sits down. She lifts up her shirt, and she is deeply bruised all over with severe internal bleeding. Dying, she remembers when she and Chris decided to take some of the money. We see more of the scene: Erin is tired of their shitty lives, and she wants to escape. Chris is hesitant, but Erin begs him to do it for her. He agrees. We see another flashback, to Erin and a young Shelby on that camping trip. In the present, Erin looks out at the sky and dies.