Using first-party anonymous IDs as Meta external_id before login
Updated July 14, 2026 · 9 min read
A first-party anonymous ID is the closest web equivalent of the careful anonymous identity discipline mobile teams use before registration. It gives the server a stable pre-login key that can survive page views, paywall opens, checkout starts, and verified purchases.
The goal is not to replace _fbp or _fbc. The goal is to add a durable first-party join key to Meta CAPI user_data so pre-auth and post-auth events do not look like unrelated users.
Why anonymous external_id matters
Most paid funnels do not start after login. The ad click, landing page view, pricing page, paywall exposure, and InitiateCheckout often happen while the visitor is anonymous. If the server only starts sending external_id after account creation, Meta receives a broken chain: anonymous upper-funnel events and identified lower-funnel events.
A stable anonymous ID closes that gap. Hash it and send it as external_id on server events before login, then continue sending the same identifier alongside the authenticated user ID after login until the identity merge is complete.
Identity invariants
- visitor_id is stable across sessions until cookie deletion, logout reset policy, or explicit privacy reset.
- session_id is short-lived and never used as the only external_id for conversion events.
- authenticated_user_id is introduced after login but does not cause a new visitor_id to be minted.
- email and phone are normalized and hashed only when the user has provided them.
- Purchase, Subscribe, and StartTrial events use the same identity graph as ViewContent and InitiateCheckout, not a webhook-only customer ID.
Hashing and sending anonymous external_id
Meta matching fields should be normalized and hashed consistently. The anonymous ID should be first-party, not a copied ad-platform identifier.
import { createHash } from "node:crypto";
function sha256(value: string) {
return createHash("sha256").update(value.trim().toLowerCase()).digest("hex");
}
const userData = {
external_id: [sha256(visitorId), userId ? sha256(userId) : undefined].filter(Boolean),
em: email ? sha256(email) : undefined,
fbp: identity.fbp,
fbc: identity.fbc,
client_ip_address: identity.ipAddress,
client_user_agent: identity.userAgent,
};Merge protocol
- 1Create visitor_id on the first eligible request and store it in a same-site first-party cookie.
- 2Mirror the visitor_id into the server landing session so webhooks can recover it without browser cookies.
- 3On signup or login, attach visitor_id to the user record as a recent anonymous identity, not as a permanent account ID.
- 4For a grace window, send both hashed visitor_id and hashed user_id as external_id values so Meta can bridge the transition.
- 5For purchases, derive the event identity from the checkout/order row that captured visitor_id, not only from the billing provider customer object.
Frequently asked questions
Should anonymous visitor IDs be sent as external_id?
For advanced server-side attribution, yes, if your privacy policy and consent model allow it. The value should be first-party, stable, and hashed before being sent to Meta.
Is anonymous external_id more important than _fbp and _fbc?
No. On web, _fbp and _fbc remain primary Meta web identity fields. Anonymous external_id complements them by giving your server a durable first-party join key across pre-login and post-login events.
When should visitor_id reset?
Reset on explicit privacy actions, cookie deletion, or a product-defined logout boundary if accounts can be shared. Do not reset it on every page view, every checkout attempt, or every app launch.
Related guides
Not sure which signal path is breaking?
Zoruko audits your web and mobile code for broken Meta and TikTok signal chains, then ships each fix as a pull request you review.
Run a free audit