Why it happens
The purchase came from a server without the buyer's session. GA4 takes a session's source from the start of the session, in the buyer's browser. A purchase sent from a server through the Measurement Protocol joins that session only when it carries the client ID and the session ID the Google tag set in the browser. Google's help says that when session-based Measurement Protocol events show as "(not set) / (not set)", you should send the session_id with a valid value from the client-side event. A purchase with an ID made up on the server, or with no session ID, has no source to take, and GA4 puts it in Unassigned.
The session has no session_start. Google's help says session source and medium show "(not set)" for sessions without the session_start event, and that this traffic is shown as Unassigned in the default channel group. One cause Google lists is events sent with denied consent after the user first granted it.
The shopper declined analytics cookies. When analytics_storage is denied, Google says the tag does not read or write first-party analytics cookies, and sends cookieless pings instead. Without the cookie there is no stored client ID that ties the purchase to the visit where the shopper arrived from an ad. Google uses these pings for modeling, which fills some gaps in reports.
Part of the checkout runs on another domain. Google's cross-domain help says each domain sets its own cookies. One person who visits two root domains on the same device counts as two users with two sessions. If a checkout or payment step runs on another root domain and cross-domain measurement is not set up for it, the purchase can land in a session with no link to the ad visit. When that other domain shows up as the source instead, see Your checkout or payment provider shows up as a referral in GA4.
The session timed out before the purchase. By default a GA4 session ends after 30 minutes without activity. A shopper who leaves the tab open and pays later starts a new session, and that session's source decides the channel. Google counts a visit from a saved link or a typed address as Direct.
The purchase was sent late. Google lets Measurement Protocol events be backdated up to 72 hours. In the default setting, an event with an older timestamp is accepted and dated 72 hours ago. To join data from the Google tag, Google says the event should arrive within 48 hours of the original browser event. A purchase sent after a delay may not join the buyer's visit.
The campaign tags match no channel rule. Unassigned is the value GA4 uses when no channel rule matches the event data. A utm_medium that no rule knows, such as a word you made up for one campaign, ends up there.
Check it yourself
- 1In GA4, open Reports > Acquisition > Traffic acquisition. Set the last 7 full days. Read the rows Direct and Unassigned and their revenue.
- 2Change the primary dimension to Session source / medium. Direct is "(direct) / (none)" or "(direct) / (not set)". A row "(not set) / (not set)" is a session GA4 could not give a source.
- 3In GA4 > Explore, start a Free form exploration. Add the dimensions Session default channel group and Transaction ID, and the metrics Ecommerce purchases and Purchase revenue. Set the same days.
- 4Pick a few transaction IDs from Direct and Unassigned. Open the same orders in Shopify admin > Orders and look at where Shopify says the buyer's visits came from. An order Shopify ties to an ad that GA4 shows as Unassigned lost its session on the way.
- 5Find every sender of purchases. In GA4 Admin > Data collection and modification > Data streams, open your web stream and look at Measurement Protocol API secrets. Each secret can belong to a tool that sends events from a server. Ask each tool whether its purchase carries the browser's client ID and session ID.
- 6On the same web stream, open Configure tag settings > Show all > Adjust session timeout and read the timeout.
- 7Under Configure tag settings > Show all > Configure your domains, check that every domain of your store and checkout is listed.
- 8In GA4 Admin > Data collection and modification > Consent settings, check what the property reports about consent signals.
- 9Place a test order and look for it in the GA4 Realtime report. Check its channel in Explore the next day.
Why a server purchase still needs the browser's identifiers is in Shopify server-side tracking.
FAQ
Is Unassigned the same as Direct?
No. Direct is a rule: the source is "(direct)" and the medium is "(none)" or "(not set)". Unassigned is what GA4 shows when no rule matches.
Do Direct and Unassigned purchases still count revenue?
Yes. The purchase and its revenue are in your reports. Only the channel is missing.
Does a server-side purchase always lose its source?
No. Google says a valid session_id connects Measurement Protocol data with the session data the Google tag collected. The purchase then takes the source of that session.
Will GA4 ever match Shopify's view of the source?
Not order by order. Shopify and GA4 record visits on their own, with their own rules and consent handling.