Twilio Segment
Segment is a pipe, not a single tracker — analytics.js fans one call out to every downstream tool in your workspace, which makes coarse script-gating a blunt instrument. Consenti gives you a per-call consent breakdown instead, so identify() can stay blocked while page() already flows, or vice versa.
Reading consent in Segment's shape
const segmentConsent = widget.getConsent('twilio-segment')
// {
// identify: 'denied', // ties events to a known user — from cookies with purpose: 'preferences'
// page: 'granted', // page-view calls — from cookies with purpose: 'analytics'
// track: 'granted', // event tracking — from cookies with purpose: 'analytics'
// group: 'granted', // account/org association — from cookies with purpose: 'analytics'
// alias: 'granted', // merging anon + known IDs — from cookies with purpose: 'analytics'
// }identify is deliberately split out under the preferences purpose rather than analytics— it's the call that attaches PII (email, user ID, traits) to the visitor, so it's reasonable for a visitor to allow anonymous page/track events while still declining to be identified. See API Methods for the full getConsent(type) reference.Step 1 — Gate the snippet load
Don't call analytics.load() at all until at least anonymous tracking is allowed — this also means the Segment CDN request itself, and every downstream destination it can trigger, never happens pre-consent. The categoryId below is your own authored category ID (see Category type) — use whichever one covers analytics in your profile:
import { CategoryScript } from '@consenti/ui'
new CategoryScript({
categoryId: 'analytics',
widget,
unsafeInnerHTML: `
!function(){var analytics=window.analytics=window.analytics||[];
if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");
else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];
analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};
for(var e=0;e<analytics.methods.length;e++){var key=analytics.methods[e];analytics[key]=analytics.factory(key)}
analytics.load=function(key,e){var t=document.createElement("script");t.type="text/javascript";t.async=!0;
t.src="https://cdn.segment.com/analytics.js/v1/"+key+"/analytics.min.js";
var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._loadOptions=e};
analytics._writeKey="YOUR_WRITE_KEY";analytics.SNIPPET_VERSION="4.16.1";
analytics.load("YOUR_WRITE_KEY");}}();
`,
})Step 2 — Wrap each call site with its own check
Segment's SDK has no built-in consent flag, so per-call gating means checking getConsent('twilio-segment') at the point you call each method. A small wrapper keeps this out of your feature code:
function segmentConsent() {
return widget.getConsent('twilio-segment')
}
export function safeIdentify(userId: string, traits?: Record<string, unknown>) {
if (segmentConsent()?.identify === 'granted') {
window.analytics?.identify(userId, traits)
}
}
export function safePage(name?: string, properties?: Record<string, unknown>) {
if (segmentConsent()?.page === 'granted') {
window.analytics?.page(name, properties)
}
}
export function safeTrack(event: string, properties?: Record<string, unknown>) {
if (segmentConsent()?.track === 'granted') {
window.analytics?.track(event, properties)
}
}Call site usage looks identical to calling Segment directly:
safeTrack('Product Viewed', { productId: 'sku_123' })
safeIdentify(user.id, { email: user.email, plan: user.plan })getConsent() always reflects the latest submission.Backfilling identify() after consent arrives
A common pattern: track anonymously from page load, then call identify() once the visitor logs in or grants the cookie parameter carrying purpose: 'preferences' — whichever happens second. ConsentAction's id here is a specific cookie ID (unlike CategoryAction/CategoryScript's id/categoryId, which target a whole authored category):
import { ConsentAction } from '@consenti/ui'
new ConsentAction({
id: 'preferences_storage', // the cookie ID you gave purpose: 'preferences' in your profile
widget,
onGrant: () => {
if (currentUser) safeIdentify(currentUser.id, { email: currentUser.email })
},
})Verifying
- Open the browser console and inspect the Segment debugger panel, or run
analytics.debug(). - Before consenting, confirm no requests to
api.segment.iofire. - Accept analytics consent — confirm
page/trackcalls start reaching the debugger, whileidentifystays absent until you've also granted the category covering yourpreferences-purpose cookie.