Self-Generated Vendor IDs
Self-generation is Signal’s primary method for establishing vendor identity without loading vendor JavaScript. The Ingestion Gateway generates identifiers in the exact format each vendor expects, sets them as server-side first-party cookies, and stores them in Redis. When events are delivered to vendor APIs, these self-generated IDs are included in the payload — and vendors accept them as valid.
How It Works
Each vendor’s server-side API defines a format for its client identifier. For example, Google Analytics 4’s Measurement Protocol expects a client_id in the format {random_uint32}.{unix_timestamp}. Meta’s Conversions API expects an _fbp value in the format fb.1.{timestamp_ms}.{random_10_digit}.
Signal generates IDs that match these formats exactly. Because the vendor APIs are designed for server-to-server use, they accept any correctly-formatted identifier — there is no validation that the ID was generated by the vendor’s own JavaScript.
The generated IDs are set as first-party cookies on the customer’s subdomain via Set-Cookie response headers. This achieves two goals:
- Persistence — the ID survives across sessions and page loads
- ITP exemption — server-set cookies are not subject to Safari’s 7-day cap
Supported Vendors
| Vendor | Cookie Name | Format | TTL | Notes |
|---|---|---|---|---|
| GA4 | _ga | {random_uint32}.{unix_timestamp} | 2 years | Measurement Protocol accepts any client_id |
| Meta | _fbp | fb.1.{timestamp_ms}.{random_10_digit} | 90 days | Conversions API accepts self-generated fbp |
| Meta | _fbc | fb.1.{timestamp_ms}.{fbclid} | 90 days | Only generated when fbclid is present in URL |
| TikTok | _ttp | UUID v4 (lowercase, no dashes, 27 chars via nanoid) | 13 months | Events API accepts self-generated ttp |
_pin_unauth | UUID v4 | 365 days | Conversions API accepts self-generated value | |
| Snapchat | _scid | UUID v4 | 13 months | Conversions API accepts self-generated value |
Generation Details
GA4 (_ga)
Google Analytics 4’s Measurement Protocol requires a client_id parameter. The standard GA4 JavaScript tag generates this as two dot-separated numbers: a random 32-bit unsigned integer and a Unix timestamp.
Format: {random_uint32}.{unix_timestamp}
Example: 1234567890.1708876543Signal generates this at the Ingestion Gateway:
clientID := fmt.Sprintf("%d.%d", cryptoRandUint32(), time.Now().Unix())The Measurement Protocol does not validate whether the client_id was generated by its own tag. Any value in the correct format is accepted as a valid client identifier.
If the standard GA4 tag also runs on the page (during a migration period, for example), both Signal’s server-set _ga cookie and GA4’s JavaScript-set _ga cookie will be present. Signal’s server-set cookie takes precedence because it is set first during the response, and Datafly.js uses the server-set value when sending events.
Meta (_fbp)
Meta’s Conversions API accepts an fbp parameter representing the browser ID. The Meta pixel generates this in a specific format:
Format: fb.{subdomain_index}.{creation_time_ms}.{random_10_digit}
Example: fb.1.1708876543000.9876543210Signal generates this as:
fbp := fmt.Sprintf("fb.1.%d.%d", time.Now().UnixMilli(), cryptoRandNDigits(10))The subdomain index is always 1 for standard implementations. The Conversions API accepts self-generated fbp values — Meta’s own documentation acknowledges that server-side implementations may generate this value independently.
Meta (_fbc)
The _fbc cookie represents a Facebook click ID and is only generated when the fbclid URL parameter is present:
Format: fb.1.{timestamp_ms}.{fbclid_value}
Example: fb.1.1708876543000.IwAR3x...if fbclid := queryParams.Get("fbclid"); fbclid != "" {
fbc := fmt.Sprintf("fb.1.%d.%s", time.Now().UnixMilli(), fbclid)
}The _fbc cookie is only set when a fbclid parameter is found in the URL. It is not generated on requests without a click ID. See Click ID Capture for how click IDs are detected.
TikTok (_ttp)
TikTok’s Events API accepts a ttp parameter for browser identity. The TikTok pixel generates this as a 27-character lowercase alphanumeric string (similar to a UUID v4 without dashes, truncated):
Format: 27-character nanoid (lowercase alphanumeric)
Example: a1b2c3d4e5f6g7h8i9j0k1l2m3nttp := nanoid.MustGenerate("0123456789abcdefghijklmnopqrstuvwxyz", 27)Pinterest (_pin_unauth)
Pinterest’s Conversions API accepts an external_id that maps to the _pin_unauth cookie. The format is a standard UUID v4:
Format: UUID v4
Example: f47ac10b-58cc-4372-a567-0d02b2c3d479pinUnauth := uuid.NewV4().String()Snapchat (_scid)
Snapchat’s Conversions API accepts a uuid_c1 parameter mapped from the _scid cookie. The format is a standard UUID v4:
Format: UUID v4
Example: b82d5c91-3f7a-4e28-9c8f-1a2b3c4d5e6fscid := uuid.NewV4().String()Cookie Setting
All self-generated IDs are set as server-side cookies in the Ingestion Gateway response. A single response may include multiple Set-Cookie headers:
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: _dfid=f47ac10b-58cc-4372-a567-0d02b2c3d479; Max-Age=63072000; HttpOnly; Secure; SameSite=Lax; Path=/
Set-Cookie: _ga=1234567890.1708876543; Max-Age=63072000; Secure; SameSite=Lax; Path=/
Set-Cookie: _fbp=fb.1.1708876543000.9876543210; Max-Age=7776000; Secure; SameSite=Lax; Path=/
Set-Cookie: _ttp=a1b2c3d4e5f6g7h8i9j0k1l2m3n; Max-Age=34164000; Secure; SameSite=Lax; Path=/Note that vendor cookies are not httpOnly — this matches the behaviour of the original vendor tags, which set cookies accessible to JavaScript. The _dfid cookie is the only one marked httpOnly.
Vendor cookies are only set for vendors that have an active integration on the source. If a source only has GA4 and Meta integrations, the gateway will not set _ttp, _pin_unauth, or _scid cookies.
Why This Works
Vendor server-side APIs are explicitly designed for server-to-server event delivery. They accept externally-generated client identifiers because:
- Server-side implementations have no browser context — a backend sending events cannot run vendor JavaScript, so the API must accept IDs generated by other means.
- The ID is a correlation key, not a credential — vendors use the client ID to group events from the same user, not to authenticate the sender. Any consistent value serves this purpose.
- Vendor documentation endorses it — Google, Meta, TikTok, and others explicitly document that server-side implementations should generate their own client identifiers.
The key requirement is consistency: the same ID must be sent with every event from the same visitor. Signal achieves this by persisting the ID in both a first-party cookie (for browser-to-server consistency) and Redis (for server-side lookup during event processing).
Idempotent Generation
The Ingestion Gateway only generates a vendor ID if one does not already exist. On each request:
- Read existing vendor cookies from the
Cookieheader - For each enabled vendor integration on this source:
- If the vendor cookie exists in the request, use the existing value
- If the vendor cookie is missing, generate a new one
- Store all values (existing and new) in Redis
- Set
Set-Cookieheaders for any new or refreshed cookies
This means a visitor who already has a _ga cookie from a previous visit will continue using the same value. New vendor IDs are only generated when a cookie is missing — either because the visitor is new, or because the cookie expired.